galath 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/README.md +206 -0
- package/TODO.md +140 -0
- package/index.html +188 -0
- package/logo.jpg +0 -0
- package/logo.svg +96 -0
- package/package.json +32 -0
- package/packages/galath/package.json +28 -0
- package/packages/galath/src/behavior.js +193 -0
- package/packages/galath/src/binding.js +247 -0
- package/packages/galath/src/boot.js +52 -0
- package/packages/galath/src/command.js +117 -0
- package/packages/galath/src/component.js +505 -0
- package/packages/galath/src/controller.js +181 -0
- package/packages/galath/src/core.js +190 -0
- package/packages/galath/src/imports.js +132 -0
- package/packages/galath/src/index.js +38 -0
- package/packages/galath/src/instance-model.js +343 -0
- package/packages/galath/src/morph.js +237 -0
- package/packages/galath/src/rendering.js +556 -0
- package/packages/galath/src/signals.js +215 -0
- package/packages/galath/src/templates.js +24 -0
- package/packages/galath/src/xml-events.js +53 -0
- package/packages/galath-css/css/bootstrap-icons.min.css +5 -0
- package/packages/galath-css/css/bootstrap.min.css +6 -0
- package/packages/galath-css/css/fonts/bootstrap-icons.json +2077 -0
- package/packages/galath-css/css/fonts/bootstrap-icons.woff +0 -0
- package/packages/galath-css/css/fonts/bootstrap-icons.woff2 +0 -0
- package/packages/galath-css/js/bootstrap.bundle.min.js +7 -0
- package/packages/galath-css/package.json +13 -0
- package/playground/app.xml +214 -0
- package/playground/chapters/01-welcome.xml +94 -0
- package/playground/chapters/02-signals.xml +166 -0
- package/playground/chapters/03-instance.xml +130 -0
- package/playground/chapters/04-bindings.xml +156 -0
- package/playground/chapters/05-lists.xml +138 -0
- package/playground/chapters/06-commands.xml +144 -0
- package/playground/chapters/07-controller.xml +115 -0
- package/playground/chapters/08-events.xml +126 -0
- package/playground/chapters/09-behaviors.xml +210 -0
- package/playground/chapters/10-components.xml +152 -0
- package/playground/chapters/11-imports.xml +108 -0
- package/playground/chapters/12-expressions.xml +161 -0
- package/playground/chapters/13-paths.xml +197 -0
- package/playground/components/chapter-shell.xml +29 -0
- package/playground/components/highlighter.js +111 -0
- package/playground/components/run-snippet.js +120 -0
- package/public/basic/bootstrap-icons.min.css +5 -0
- package/public/basic/bootstrap.bundle.min.js +7 -0
- package/public/basic/bootstrap.min.css +6 -0
- package/public/basic/fonts/bootstrap-icons.json +2077 -0
- package/public/basic/fonts/bootstrap-icons.woff +0 -0
- package/public/basic/fonts/bootstrap-icons.woff2 +0 -0
- package/public/basic/theme.css +209 -0
- package/seed.html +321 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
chapters/02-signals.xml
|
|
3
|
+
|
|
4
|
+
Signals are reactive cells. This chapter introduces them through three
|
|
5
|
+
worked examples:
|
|
6
|
+
|
|
7
|
+
1. A counter signal with two buttons.
|
|
8
|
+
2. A computed signal derived from another signal.
|
|
9
|
+
3. A `name` signal echoed in the heading via interpolation.
|
|
10
|
+
-->
|
|
11
|
+
<galath xmlns:bind="urn:galath:bind" xmlns:on="urn:galath:on" xmlns:use="urn:galath:use" xmlns:drag="urn:galath:drag" xmlns:drop="urn:galath:drop" xmlns:class="urn:galath:class">
|
|
12
|
+
<component name="chapter-signals" tag="x-chapter-signals">
|
|
13
|
+
<model>
|
|
14
|
+
<!-- Demo state. -->
|
|
15
|
+
<signal name="count" value="0" />
|
|
16
|
+
<signal name="step" value="1" />
|
|
17
|
+
<signal name="name" value="World" />
|
|
18
|
+
|
|
19
|
+
<!--
|
|
20
|
+
A computed signal. Re-evaluated whenever `count` changes. Without
|
|
21
|
+
<computed>, you would write the same logic in every <text>
|
|
22
|
+
binding - the derived signal centralizes it once.
|
|
23
|
+
-->
|
|
24
|
+
<computed name="parity" from="count">
|
|
25
|
+
<expression>count % 2 === 0 ? 'even' : 'odd'</expression>
|
|
26
|
+
</computed>
|
|
27
|
+
|
|
28
|
+
<!--
|
|
29
|
+
Computed chain: a -> b -> c. Each computed declares its source
|
|
30
|
+
with `from=`; when `a` moves, `b` re-runs, which moves `c`. The
|
|
31
|
+
dependency direction is explicit, which keeps the data flow
|
|
32
|
+
easy to reason about (no implicit auto-tracking magic).
|
|
33
|
+
-->
|
|
34
|
+
<signal name="a" value="2" />
|
|
35
|
+
<computed name="b" from="a">
|
|
36
|
+
<expression>a * 10</expression>
|
|
37
|
+
</computed>
|
|
38
|
+
<computed name="c" from="b">
|
|
39
|
+
<expression>b + 1</expression>
|
|
40
|
+
</computed>
|
|
41
|
+
|
|
42
|
+
<signal name="snippet"><![CDATA[<component name="counter" tag="x-counter">
|
|
43
|
+
<model>
|
|
44
|
+
<signal name="count" value="0" />
|
|
45
|
+
<signal name="step" value="1" />
|
|
46
|
+
<computed name="parity" from="count">
|
|
47
|
+
<expression>count % 2 === 0 ? 'even' : 'odd'</expression>
|
|
48
|
+
</computed>
|
|
49
|
+
</model>
|
|
50
|
+
|
|
51
|
+
<view>
|
|
52
|
+
<button on:click="set('count', count + step)">+</button>
|
|
53
|
+
<button on:click="set('count', count - step)">-</button>
|
|
54
|
+
<span>Count: {count} ({parity})</span>
|
|
55
|
+
<input type="number" bind:value="step" />
|
|
56
|
+
</view>
|
|
57
|
+
</component>]]></signal>
|
|
58
|
+
</model>
|
|
59
|
+
|
|
60
|
+
<controller>
|
|
61
|
+
<action name="bump">
|
|
62
|
+
<set signal="count" value="count + step" />
|
|
63
|
+
</action>
|
|
64
|
+
<action name="bumpDown">
|
|
65
|
+
<set signal="count" value="count - step" />
|
|
66
|
+
</action>
|
|
67
|
+
</controller>
|
|
68
|
+
|
|
69
|
+
<view>
|
|
70
|
+
<div class="px-4 py-5">
|
|
71
|
+
<x-chapter-heading icon="bi-broadcast" title="Signals"
|
|
72
|
+
subtitle="The reactive cell. Every dynamic value in Galath is a signal." />
|
|
73
|
+
|
|
74
|
+
<div class="row g-4 mb-4">
|
|
75
|
+
<div class="col-lg-6">
|
|
76
|
+
<div class="card xes-card h-100">
|
|
77
|
+
<div class="card-body">
|
|
78
|
+
<h2 class="h5"><i class="bi bi-journal-text me-2 text-info"></i>How it works</h2>
|
|
79
|
+
<p class="text-secondary">
|
|
80
|
+
A <strong>signal</strong> holds a value. Read it by name in any expression
|
|
81
|
+
(<code>count + 1</code>); write it via <code>set('count', ...)</code> in a
|
|
82
|
+
handler or with <code><set signal="count" value="..."/></code> in a
|
|
83
|
+
controller action.
|
|
84
|
+
</p>
|
|
85
|
+
<p class="text-secondary">
|
|
86
|
+
A <strong>computed</strong> signal is derived from another signal. It
|
|
87
|
+
re-evaluates automatically when the source changes. Use it to centralize
|
|
88
|
+
formulas you would otherwise repeat across the view.
|
|
89
|
+
</p>
|
|
90
|
+
<p class="text-secondary mb-0">
|
|
91
|
+
When ANY signal in the component scope changes, the renderer schedules a
|
|
92
|
+
microtask-batched re-render. You never call "update".
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="col-lg-6">
|
|
98
|
+
<div class="card xes-card h-100">
|
|
99
|
+
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
100
|
+
<strong><i class="bi bi-code-slash me-2"></i>Source</strong>
|
|
101
|
+
<div class="btn-group btn-group-sm" role="group"><button class="btn btn-outline-info" on:click="window.galathRunSnippet(snippet)" title="Open this snippet in a new tab"><i class="bi bi-play-fill me-1"></i>Run</button><button class="btn btn-outline-info" use:copy="snippet" title="Copy to clipboard"><i class="bi bi-clipboard me-1"></i>Copy</button></div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="card-body p-0">
|
|
104
|
+
<pre class="xes-code rounded p-3 mb-0"><code><text value="snippet" /></code></pre>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="card xes-card">
|
|
111
|
+
<div class="card-header bg-transparent">
|
|
112
|
+
<strong><i class="bi bi-play-circle me-2"></i>Live demo</strong>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="card-body">
|
|
115
|
+
<div class="row g-3 align-items-end">
|
|
116
|
+
<div class="col-md-4">
|
|
117
|
+
<label class="form-label">Greet someone</label>
|
|
118
|
+
<input class="form-control" bind:value="name" />
|
|
119
|
+
</div>
|
|
120
|
+
<div class="col-md-4">
|
|
121
|
+
<label class="form-label">Step size</label>
|
|
122
|
+
<input class="form-control" type="number" bind:value="step" />
|
|
123
|
+
</div>
|
|
124
|
+
<div class="col-md-4 d-flex gap-2">
|
|
125
|
+
<button class="btn btn-info flex-grow-1" on:click="#bump">+</button>
|
|
126
|
+
<button class="btn btn-outline-light flex-grow-1" on:click="#bumpDown">-</button>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<hr class="my-4" />
|
|
131
|
+
|
|
132
|
+
<div class="d-flex flex-column gap-2">
|
|
133
|
+
<div class="display-6 mb-0">Hello, {name}.</div>
|
|
134
|
+
<div class="text-secondary">
|
|
135
|
+
Count: <span class="badge text-bg-info">{count}</span>
|
|
136
|
+
<span class="ms-2">parity: <span class="badge text-bg-secondary">{parity}</span></span>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div class="card xes-card mt-4">
|
|
143
|
+
<div class="card-header bg-transparent">
|
|
144
|
+
<strong><i class="bi bi-diagram-3 me-2"></i>Computed chain: a → b → c</strong>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="card-body">
|
|
147
|
+
<p class="text-secondary">
|
|
148
|
+
Each derived signal declares its source with <code>from=</code>. When you change
|
|
149
|
+
<code>a</code>, <code>b = a * 10</code> updates, then <code>c = b + 1</code>
|
|
150
|
+
follows. Dependency tracking is <em>explicit</em>, not implicit - one less thing
|
|
151
|
+
to debug.
|
|
152
|
+
</p>
|
|
153
|
+
<div class="row g-3 align-items-end">
|
|
154
|
+
<div class="col-md-3">
|
|
155
|
+
<label class="form-label">a</label>
|
|
156
|
+
<input class="form-control" type="number" bind:value="a" />
|
|
157
|
+
</div>
|
|
158
|
+
<div class="col-md-3"><div class="text-secondary small">b = a * 10</div><div class="display-6">{b}</div></div>
|
|
159
|
+
<div class="col-md-3"><div class="text-secondary small">c = b + 1</div><div class="display-6">{c}</div></div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</view>
|
|
165
|
+
</component>
|
|
166
|
+
</galath>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
chapters/03-instance.xml
|
|
3
|
+
|
|
4
|
+
The instance tree: state shaped as XML you can navigate by path.
|
|
5
|
+
|
|
6
|
+
Demo: a simple "settings" instance with nested elements; we read and
|
|
7
|
+
write attribute values from the view and show the live tree as
|
|
8
|
+
serialized XML.
|
|
9
|
+
-->
|
|
10
|
+
<galath xmlns:bind="urn:galath:bind" xmlns:on="urn:galath:on" xmlns:use="urn:galath:use" xmlns:drag="urn:galath:drag" xmlns:drop="urn:galath:drop" xmlns:class="urn:galath:class">
|
|
11
|
+
<component name="chapter-instance" tag="x-chapter-instance">
|
|
12
|
+
<model>
|
|
13
|
+
<instance>
|
|
14
|
+
<user name="Ada" theme="dark">
|
|
15
|
+
<prefs>
|
|
16
|
+
<pref key="emails" value="weekly" />
|
|
17
|
+
<pref key="autosave" value="true" />
|
|
18
|
+
</prefs>
|
|
19
|
+
</user>
|
|
20
|
+
</instance>
|
|
21
|
+
|
|
22
|
+
<signal name="snippet"><![CDATA[<model>
|
|
23
|
+
<instance>
|
|
24
|
+
<user name="Ada" theme="dark">
|
|
25
|
+
<prefs>
|
|
26
|
+
<pref key="emails" value="weekly" />
|
|
27
|
+
</prefs>
|
|
28
|
+
</user>
|
|
29
|
+
</instance>
|
|
30
|
+
</model>
|
|
31
|
+
|
|
32
|
+
<input bind:value="/user/@name" />
|
|
33
|
+
<select bind:value="/user/@theme">
|
|
34
|
+
<option value="dark">Dark</option>
|
|
35
|
+
<option value="light">Light</option>
|
|
36
|
+
</select>
|
|
37
|
+
|
|
38
|
+
<repeat ref="/user/prefs/pref" as="p">
|
|
39
|
+
<div>
|
|
40
|
+
<text value="$p/@key" />: <text value="$p/@value" />
|
|
41
|
+
</div>
|
|
42
|
+
</repeat>]]></signal>
|
|
43
|
+
</model>
|
|
44
|
+
|
|
45
|
+
<view>
|
|
46
|
+
<div class="px-4 py-5">
|
|
47
|
+
<x-chapter-heading icon="bi-database" title="Instance Tree"
|
|
48
|
+
subtitle="Application state lives as XML. Bind to attributes by path." />
|
|
49
|
+
|
|
50
|
+
<div class="row g-4 mb-4">
|
|
51
|
+
<div class="col-lg-6">
|
|
52
|
+
<div class="card xes-card h-100">
|
|
53
|
+
<div class="card-body">
|
|
54
|
+
<h2 class="h5"><i class="bi bi-journal-text me-2 text-info"></i>How it works</h2>
|
|
55
|
+
<p class="text-secondary">
|
|
56
|
+
The <code><instance></code> block defines initial state as XML. Every
|
|
57
|
+
attribute becomes a reactive signal under the hood, so any change is observed
|
|
58
|
+
by the renderer.
|
|
59
|
+
</p>
|
|
60
|
+
<p class="text-secondary">
|
|
61
|
+
Paths look like a strict subset of XPath:
|
|
62
|
+
</p>
|
|
63
|
+
<ul class="text-secondary mb-0">
|
|
64
|
+
<li><code>/user/@name</code> - root, child, attribute.</li>
|
|
65
|
+
<li><code>/user/prefs/pref[@key=emails]/@value</code> - predicate selector.</li>
|
|
66
|
+
<li><code>$p/@key</code> - path under a local variable bound by <code><repeat as="p"></code>.</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="col-lg-6">
|
|
72
|
+
<div class="card xes-card h-100">
|
|
73
|
+
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
74
|
+
<strong><i class="bi bi-code-slash me-2"></i>Source</strong>
|
|
75
|
+
<div class="btn-group btn-group-sm" role="group"><button class="btn btn-outline-info" on:click="window.galathRunSnippet(snippet)" title="Open this snippet in a new tab"><i class="bi bi-play-fill me-1"></i>Run</button><button class="btn btn-outline-info" use:copy="snippet" title="Copy to clipboard"><i class="bi bi-clipboard me-1"></i>Copy</button></div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="card-body p-0">
|
|
78
|
+
<pre class="xes-code rounded p-3 mb-0"><code><text value="snippet" /></code></pre>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="card xes-card">
|
|
85
|
+
<div class="card-header bg-transparent">
|
|
86
|
+
<strong><i class="bi bi-play-circle me-2"></i>Live demo</strong>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="card-body">
|
|
89
|
+
<div class="row g-3">
|
|
90
|
+
<div class="col-md-4">
|
|
91
|
+
<label class="form-label">Name</label>
|
|
92
|
+
<input class="form-control" bind:value="/user/@name" />
|
|
93
|
+
</div>
|
|
94
|
+
<div class="col-md-4">
|
|
95
|
+
<label class="form-label">Theme</label>
|
|
96
|
+
<select class="form-select" bind:value="/user/@theme">
|
|
97
|
+
<option value="dark">Dark</option>
|
|
98
|
+
<option value="light">Light</option>
|
|
99
|
+
<option value="contrast">High contrast</option>
|
|
100
|
+
</select>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<hr class="my-4" />
|
|
105
|
+
|
|
106
|
+
<h3 class="h6 text-uppercase text-secondary">Preferences</h3>
|
|
107
|
+
<div class="d-flex flex-column gap-2">
|
|
108
|
+
<repeat ref="/user/prefs/pref" as="p">
|
|
109
|
+
<div class="input-group">
|
|
110
|
+
<span class="input-group-text" style="min-width: 7rem;">
|
|
111
|
+
<text value="$p/@key" />
|
|
112
|
+
</span>
|
|
113
|
+
<input class="form-control" bind:value="$p/@value" />
|
|
114
|
+
</div>
|
|
115
|
+
</repeat>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<hr class="my-4" />
|
|
119
|
+
|
|
120
|
+
<div class="alert alert-info mb-0">
|
|
121
|
+
<i class="bi bi-info-circle me-1"></i>
|
|
122
|
+
Hello <strong>{valueOf('/user/@name')}</strong>! Your theme is <code>{valueOf('/user/@theme')}</code>.
|
|
123
|
+
Edit the inputs above and watch this line update without any explicit binding code.
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</view>
|
|
129
|
+
</component>
|
|
130
|
+
</galath>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
chapters/04-bindings.xml
|
|
3
|
+
|
|
4
|
+
Directives for connecting view elements to state and behavior:
|
|
5
|
+
|
|
6
|
+
bind:property="signal|path" two-way value/checked/etc.
|
|
7
|
+
on:event="code | #action" one-way event handler
|
|
8
|
+
class:name="expr" toggle a class
|
|
9
|
+
{expr} attribute interpolation
|
|
10
|
+
-->
|
|
11
|
+
<galath xmlns:bind="urn:galath:bind" xmlns:on="urn:galath:on" xmlns:use="urn:galath:use" xmlns:drag="urn:galath:drag" xmlns:drop="urn:galath:drop" xmlns:class="urn:galath:class">
|
|
12
|
+
<component name="chapter-bindings" tag="x-chapter-bindings">
|
|
13
|
+
<model>
|
|
14
|
+
<signal name="email" value="ada@example.com" />
|
|
15
|
+
<signal name="newsletter" value="true" />
|
|
16
|
+
<signal name="strength" value="3" />
|
|
17
|
+
<signal name="role" value="editor" />
|
|
18
|
+
<signal name="dueDate" value="2026-06-01" />
|
|
19
|
+
<signal name="snippet"><![CDATA[<input bind:value="email" />
|
|
20
|
+
<input type="checkbox" bind:checked="newsletter" />
|
|
21
|
+
|
|
22
|
+
<button on:click="set('count', count + 1)">+</button>
|
|
23
|
+
<button on:click="#submit">Submit</button>
|
|
24
|
+
|
|
25
|
+
<div class:active="email.length > 0">
|
|
26
|
+
...
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<select bind:value="role">
|
|
30
|
+
<option value="viewer">Viewer</option>
|
|
31
|
+
<option value="editor">Editor</option>
|
|
32
|
+
<option value="admin">Admin</option>
|
|
33
|
+
</select>
|
|
34
|
+
|
|
35
|
+
<input type="date" bind:value="dueDate" />]]></signal>
|
|
36
|
+
</model>
|
|
37
|
+
|
|
38
|
+
<controller>
|
|
39
|
+
<action name="logSubmit">
|
|
40
|
+
<log value="'Submitted ' + email" />
|
|
41
|
+
</action>
|
|
42
|
+
</controller>
|
|
43
|
+
|
|
44
|
+
<view>
|
|
45
|
+
<div class="px-4 py-5">
|
|
46
|
+
<x-chapter-heading icon="bi-link-45deg" title="Bindings"
|
|
47
|
+
subtitle="Connect view elements to signals, paths, and event handlers." />
|
|
48
|
+
|
|
49
|
+
<div class="row g-4 mb-4">
|
|
50
|
+
<div class="col-lg-6">
|
|
51
|
+
<div class="card xes-card h-100">
|
|
52
|
+
<div class="card-body">
|
|
53
|
+
<h2 class="h5"><i class="bi bi-journal-text me-2 text-info"></i>How it works</h2>
|
|
54
|
+
<ul class="text-secondary mb-3">
|
|
55
|
+
<li><code>bind:value</code>, <code>bind:checked</code> are two-way binds. Edits
|
|
56
|
+
flow back into the underlying signal or instance attribute.</li>
|
|
57
|
+
<li><code>on:click</code>, <code>on:input</code>, etc. attach event listeners.
|
|
58
|
+
The value can be inline JS (with all signals in scope) or a controller
|
|
59
|
+
reference like <code>#submit</code>.</li>
|
|
60
|
+
<li><code>class:active="expr"</code> toggles a class when the expression is
|
|
61
|
+
truthy.</li>
|
|
62
|
+
<li><code>{expr}</code> inside an attribute or text node is interpolation. It is
|
|
63
|
+
HTML-escaped automatically - safe by default.</li>
|
|
64
|
+
</ul>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="col-lg-6">
|
|
69
|
+
<div class="card xes-card h-100">
|
|
70
|
+
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
71
|
+
<strong><i class="bi bi-code-slash me-2"></i>Source</strong>
|
|
72
|
+
<div class="btn-group btn-group-sm" role="group"><button class="btn btn-outline-info" on:click="window.galathRunSnippet(snippet)" title="Open this snippet in a new tab"><i class="bi bi-play-fill me-1"></i>Run</button><button class="btn btn-outline-info" use:copy="snippet" title="Copy to clipboard"><i class="bi bi-clipboard me-1"></i>Copy</button></div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="card-body p-0">
|
|
75
|
+
<pre class="xes-code rounded p-3 mb-0"><code><text value="snippet" /></code></pre>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="card xes-card">
|
|
82
|
+
<div class="card-header bg-transparent">
|
|
83
|
+
<strong><i class="bi bi-play-circle me-2"></i>Live demo</strong>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="card-body">
|
|
86
|
+
<div class="mb-3">
|
|
87
|
+
<label class="form-label">Email</label>
|
|
88
|
+
<input class="form-control" type="email" bind:value="email" />
|
|
89
|
+
<small class="text-secondary">Live value: <code>{email}</code></small>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="form-check mb-3">
|
|
93
|
+
<input class="form-check-input" type="checkbox" id="cb-news" bind:checked="newsletter" />
|
|
94
|
+
<label class="form-check-label" for="cb-news">Subscribe to the newsletter</label>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="mb-3">
|
|
98
|
+
<label class="form-label">Password strength: {strength}</label>
|
|
99
|
+
<input class="form-range" type="range" min="0" max="5" bind:value="strength" />
|
|
100
|
+
<div class="progress" style="height: .35rem;">
|
|
101
|
+
<div class="progress-bar bg-info" style="width: {strength * 20}%"></div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div class="alert mb-3 {strength >= 4 ? 'alert-success' : strength >= 2 ? 'alert-warning' : 'alert-danger'}">
|
|
106
|
+
<i class="bi bi-shield-check me-1"></i>
|
|
107
|
+
<strong>Strength is {strength}/5</strong> -
|
|
108
|
+
<if test="strength >= 4">strong enough.</if>
|
|
109
|
+
<if test="strength === 3">acceptable but consider longer.</if>
|
|
110
|
+
<if test="strength < 3">too weak.</if>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<button class="btn btn-info" on:click="#logSubmit">
|
|
114
|
+
<i class="bi bi-send me-1"></i>Submit (logs to console)
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="card xes-card mt-4">
|
|
120
|
+
<div class="card-header bg-transparent">
|
|
121
|
+
<strong><i class="bi bi-ui-checks-grid me-2"></i>Selects and date inputs</strong>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card-body">
|
|
124
|
+
<p class="text-secondary">
|
|
125
|
+
<code>bind:value</code> works for any control with a string-shaped value -
|
|
126
|
+
including <code><select></code> and <code><input type="date"></code>.
|
|
127
|
+
The signal holds the option value or the ISO date string; the runtime keeps the
|
|
128
|
+
control in sync after a render.
|
|
129
|
+
</p>
|
|
130
|
+
<div class="row g-3 align-items-end">
|
|
131
|
+
<div class="col-md-4">
|
|
132
|
+
<label class="form-label">Role</label>
|
|
133
|
+
<select class="form-select" bind:value="role">
|
|
134
|
+
<option value="viewer">Viewer</option>
|
|
135
|
+
<option value="editor">Editor</option>
|
|
136
|
+
<option value="admin">Admin</option>
|
|
137
|
+
</select>
|
|
138
|
+
<small class="text-secondary">Selected: <code>{role}</code></small>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="col-md-4">
|
|
141
|
+
<label class="form-label">Due date</label>
|
|
142
|
+
<input class="form-control" type="date" bind:value="dueDate" />
|
|
143
|
+
<small class="text-secondary">Picked: <code>{dueDate}</code></small>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="col-md-4">
|
|
146
|
+
<div class="alert mb-0 alert-info">
|
|
147
|
+
Granting <strong>{role}</strong> access until <strong>{dueDate}</strong>.
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</view>
|
|
155
|
+
</component>
|
|
156
|
+
</galath>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
chapters/05-lists.xml
|
|
3
|
+
|
|
4
|
+
<repeat> and <items> + <datatemplate>. Both iterate over a node-set
|
|
5
|
+
resolved from a path; <items> uses a named template, <repeat> inlines
|
|
6
|
+
the body.
|
|
7
|
+
-->
|
|
8
|
+
<galath xmlns:bind="urn:galath:bind" xmlns:on="urn:galath:on" xmlns:use="urn:galath:use" xmlns:drag="urn:galath:drag" xmlns:drop="urn:galath:drop" xmlns:class="urn:galath:class">
|
|
9
|
+
<component name="chapter-lists" tag="x-chapter-lists">
|
|
10
|
+
<model>
|
|
11
|
+
<instance>
|
|
12
|
+
<library>
|
|
13
|
+
<book title="The Phoenix Project" author="Gene Kim" read="true" />
|
|
14
|
+
<book title="Designing Data-Intensive Applications" author="Martin Kleppmann" read="false" />
|
|
15
|
+
<book title="A Philosophy of Software Design" author="John Ousterhout" read="true" />
|
|
16
|
+
<book title="Out of the Tar Pit" author="Moseley & Marks" read="false" />
|
|
17
|
+
</library>
|
|
18
|
+
</instance>
|
|
19
|
+
|
|
20
|
+
<signal name="snippet"><![CDATA[<datatemplate name="book-row" for="book">
|
|
21
|
+
<li>
|
|
22
|
+
<input type="checkbox" bind:checked="$book/@read" />
|
|
23
|
+
<strong><text value="$book/@title" /></strong>
|
|
24
|
+
by <text value="$book/@author" />
|
|
25
|
+
</li>
|
|
26
|
+
</datatemplate>
|
|
27
|
+
|
|
28
|
+
<ul>
|
|
29
|
+
<items source="/library/book" as="book" template="book-row" />
|
|
30
|
+
</ul>
|
|
31
|
+
|
|
32
|
+
<!-- or, lower level: -->
|
|
33
|
+
<ul>
|
|
34
|
+
<repeat ref="/library/book" as="book">
|
|
35
|
+
<li><text value="$book/@title" /></li>
|
|
36
|
+
</repeat>
|
|
37
|
+
</ul>]]></signal>
|
|
38
|
+
</model>
|
|
39
|
+
|
|
40
|
+
<!--
|
|
41
|
+
A reusable row template. The renderer instantiates it once per
|
|
42
|
+
matched node and binds $book to the loop item.
|
|
43
|
+
-->
|
|
44
|
+
<datatemplate name="book-row" for="book">
|
|
45
|
+
<div class="list-group-item d-flex align-items-center gap-2">
|
|
46
|
+
<input class="form-check-input m-0" type="checkbox" bind:checked="$book/@read" />
|
|
47
|
+
<div class="flex-grow-1">
|
|
48
|
+
<div class="fw-semibold {$book/@read ? 'text-decoration-line-through text-secondary' : ''}">
|
|
49
|
+
<text value="$book/@title" />
|
|
50
|
+
</div>
|
|
51
|
+
<small class="text-secondary">by <text value="$book/@author" /></small>
|
|
52
|
+
</div>
|
|
53
|
+
<span class="badge text-bg-{$book/@read ? 'success' : 'secondary'}">
|
|
54
|
+
<if test="$book/@read">read</if>
|
|
55
|
+
<if test="!$book/@read">unread</if>
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
</datatemplate>
|
|
59
|
+
|
|
60
|
+
<view>
|
|
61
|
+
<div class="px-4 py-5">
|
|
62
|
+
<x-chapter-heading icon="bi-collection" title="Lists & Templates"
|
|
63
|
+
subtitle="Iterate over selected nodes with <repeat> or a reusable <datatemplate>." />
|
|
64
|
+
|
|
65
|
+
<div class="row g-4 mb-4">
|
|
66
|
+
<div class="col-lg-6">
|
|
67
|
+
<div class="card xes-card h-100">
|
|
68
|
+
<div class="card-body">
|
|
69
|
+
<h2 class="h5"><i class="bi bi-journal-text me-2 text-info"></i>How it works</h2>
|
|
70
|
+
<p class="text-secondary">
|
|
71
|
+
<code><repeat ref="..." as="x"></code> renders its children once per node
|
|
72
|
+
in the selection, with <code>$x</code> in scope.
|
|
73
|
+
</p>
|
|
74
|
+
<p class="text-secondary">
|
|
75
|
+
<code><datatemplate name="..." for="..."></code> declares a reusable
|
|
76
|
+
fragment. <code><items source="..." template="name"/></code> instantiates it.
|
|
77
|
+
Same effect, less duplication when the row is non-trivial.
|
|
78
|
+
</p>
|
|
79
|
+
<p class="text-secondary mb-0">
|
|
80
|
+
Inside the template, <code>$x</code> is the loop item (an <code>XNode</code>).
|
|
81
|
+
Use <code>$x/@attr</code> to read an attribute, or <code>$x/text()</code> for
|
|
82
|
+
the element text.
|
|
83
|
+
</p>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="col-lg-6">
|
|
88
|
+
<div class="card xes-card h-100">
|
|
89
|
+
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
90
|
+
<strong><i class="bi bi-code-slash me-2"></i>Source</strong>
|
|
91
|
+
<div class="btn-group btn-group-sm" role="group"><button class="btn btn-outline-info" on:click="window.galathRunSnippet(snippet)" title="Open this snippet in a new tab"><i class="bi bi-play-fill me-1"></i>Run</button><button class="btn btn-outline-info" use:copy="snippet" title="Copy to clipboard"><i class="bi bi-clipboard me-1"></i>Copy</button></div>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="card-body p-0">
|
|
94
|
+
<pre class="xes-code rounded p-3 mb-0"><code><text value="snippet" /></code></pre>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="card xes-card">
|
|
101
|
+
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
102
|
+
<strong><i class="bi bi-play-circle me-2"></i>Live demo</strong>
|
|
103
|
+
<span class="badge text-bg-info">
|
|
104
|
+
{select('/library/book[@read=true]').length} / {select('/library/book').length} read
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="card-body">
|
|
108
|
+
<div class="list-group">
|
|
109
|
+
<items source="/library/book" as="book" template="book-row" />
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div class="card xes-card mt-4">
|
|
115
|
+
<div class="card-header bg-transparent">
|
|
116
|
+
<strong><i class="bi bi-list-ol me-2"></i>The implicit <code>index</code> local</strong>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="card-body">
|
|
119
|
+
<p class="text-secondary">
|
|
120
|
+
Inside a <code><repeat></code> or <code><items></code>, the runtime
|
|
121
|
+
binds <code>index</code> (zero-based) so you can number rows or alternate styling
|
|
122
|
+
without touching the data.
|
|
123
|
+
</p>
|
|
124
|
+
<ol class="list-group list-group-numbered">
|
|
125
|
+
<repeat ref="/library/book" as="book">
|
|
126
|
+
<li class="list-group-item d-flex align-items-center">
|
|
127
|
+
<span class="badge text-bg-{index % 2 === 0 ? 'info' : 'secondary'} me-2">#{index + 1}</span>
|
|
128
|
+
<span class="flex-grow-1"><text value="$book/@title" /></span>
|
|
129
|
+
<small class="text-secondary">index = {index}</small>
|
|
130
|
+
</li>
|
|
131
|
+
</repeat>
|
|
132
|
+
</ol>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</view>
|
|
137
|
+
</component>
|
|
138
|
+
</galath>
|