Repository navigation
Expand file tree
/
Copy pathreference.dj
More file actions
279 lines (190 loc) · 8.77 KB
/
Copy pathreference.dj
File metadata and controls
279 lines (190 loc) · 8.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
= Toupee Element Reference
All template elements use the `t-` prefix.
== Template Inheritance
=== `<t-extend>`
Inherit from a parent template. Must be the first non-whitespace content in a template.
```
<t-extend template="base.html">
<t-define name="title">Style Guide</t-define>
<t-define name="">Body content</t-define>
```
The `template` attribute names a template registered in the `Resolver`. Extend chains can be arbitrarily deep (up to 50 levels). Circular references are detected and produce `CircularReference`.
=== `<t-define>`
Fill a named slot when used inside `<t-extend>` or `<t-include>`.
```
<t-define name="sidebar">Sidebar content</t-define>
<t-define name="">Anonymous slot content</t-define>
```
The `name` attribute identifies which slot to fill. An empty name (`name=""`) fills the anonymous/default slot. Duplicate definitions within the same include body produce `DuplicateSlotDefinition`.
=== `<t-slot>`
Mark an insertion point in a parent or included template.
```
<t-slot name="title" />
<t-slot name="footer">Default footer content</t-slot>
<t-slot />
```
Self-closing slots render nothing if unfilled. Block slots render their default content if unfilled. Nameless slots are the anonymous/default slot.
== Variables
=== `<t-var>`
Insert a variable value with HTML escaping.
```
<t-var name="style.name" />
<t-var name="missing">fallback content</t-var>
<t-var name="title" transform="upper" />
```
Produces `UndefinedVariable` if the variable is missing and no default body or `default` transform is provided. Variable names use dot-path resolution --- `style.name` resolves through the data hierarchy (style -> name). All scalar Value types (string, integer, float, boolean) are auto-coerced to strings for rendering.
=== `<t-raw>`
Insert a variable value without HTML escaping.
```
<t-raw name="content" />
<t-raw name="missing"><em>fallback</em></t-raw>
```
Same semantics as `<t-var>` except output is not escaped. Use for pre-rendered HTML content.
=== `<t-let>`
Capture rendered content into a variable for later use.
```
<t-let name="slug" transform="slugify"><t-var name="style.name" /></t-let>
<a href="/<t-var name="slug" />"><t-var name="style.name" /></a>
```
The body is rendered, optionally transformed, and stored in the variable. The variable is available for the remainder of the current template scope.
== Attribute Binding
=== `t-var:attr`
Bind a context variable to an HTML attribute. If the variable is missing, the attribute is omitted entirely.
```
<a t-var:href="style.url" t-var:title="style.label">link</a>
```
Values are HTML-escaped.
=== `t-attr:attr`
Bind an include attribute to an HTML attribute. Same semantics as `t-var:` but reads from `attrs` instead of data.
```
<div t-attr:class="variant">content</div>
```
== Component Inclusion
=== `<t-include>`
Include another template with attribute passing and optional body.
```
<t-include template="nav.html" class="site-nav" />
<t-include template="style-card.html" variant="signature">
<t-define name="title"><h2>The Marcel Wave</h2></t-define>
<p>Layered waves with soft texture (anonymous slot)</p>
</t-include>
```
All attributes except `template` are passed as `attrs` to the included template. The body can contain `<t-define>` blocks for named slots and/or plain content for the anonymous slot.
=== `<t-attr>`
Access an include attribute inside an included template.
```
<button class="btn"><t-attr name="label" /></button>
```
Self-closing only. Renders the attribute value with HTML escaping.
== Control Flow
=== `<t-if>`
Conditional rendering based on variable, attribute, or slot existence/value.
```
<t-if var="x">shown when x exists</t-if>
<t-if var="x" equals="hello">exact match</t-if>
<t-if var="x" not-equals="hello">not equal</t-if>
<t-if var="x" not-exists>truly missing (empty string counts as existing)</t-if>
<t-if var="x" truthy>value is truthy</t-if>
<t-if var="x" not-truthy>value is falsy or missing</t-if>
<t-if var="x" contains="needle">substring match</t-if>
<t-if var="x" starts-with="/styles">prefix match</t-if>
<t-if var="x" ends-with=".html">suffix match</t-if>
<t-if var="x" matches="*.html">glob pattern match (* and ?)</t-if>
<t-if attr="variant">has variant attribute</t-if>
<t-if slot="sidebar">sidebar slot was defined</t-if>
```
Existence vs truthiness: `<t-if var="x">` is true if `x` is present, even if its value is an empty string or `false`. `not-exists` is true *only* when the variable is genuinely absent. `truthy` checks the value itself: non-empty strings, `true`, non-zero numbers, and non-empty lists/maps are truthy; empty strings, `false`, zero, empty collections, `nil`, and missing variables are falsy.
=== `<t-elif>` and `<t-else>`
Conditional branches. Must appear inside a `<t-if>` block. Self-closing separators.
```
<t-if var="a">A
<t-elif var="b" />B
<t-else />C
</t-if>
```
`<t-elif>` accepts the same attributes as `<t-if>`.
=== `<t-for>`
Iterate over a collection.
```
<t-for style in styles sort="date" order="desc" limit="5" offset="1" as loop>
<h2><t-var name="style.name" /></h2>
<span>#<t-var name="loop.number" /></span>
</t-for>
```
Attributes:
- `sort="field"` --- sort entries by the named field (ascending by default)
- `order="desc"` --- descending sort
- `limit="N"` --- limit to N entries after sorting
- `offset="N"` --- skip first N entries after sorting
- `as alias` --- expose loop metadata via `alias.*`
Loop metadata (available when `as` is used):
- `alias.index` --- 0-based position
- `alias.number` --- 1-based position
- `alias.length` --- total number of items (after limit/offset)
- `alias.first` --- `"true"` on the first iteration (absent otherwise)
- `alias.last` --- `"true"` on the last iteration (absent otherwise)
Entry fields are accessed as `prefix.field` where `prefix` is the loop variable name. Variables use dot-path resolution through the data hierarchy.
Empty or missing collections render nothing (no error).
=== For-else
`<t-else />` inside `<t-for>` provides fallback content when the collection is empty or missing:
```
<t-for style in styles>
<article><t-var name="style.name" /></article>
<t-else />
<p>No styles yet.</p>
</t-for>
```
== Other Elements
=== `<t-comment>`
Template-only comment. Content is stripped from output.
```
<t-comment>This will not appear in the rendered HTML</t-comment>
<t-comment />
```
Both block and self-closing forms are supported.
=== `<t-debug />`
Debug element. When `Options.debug` is `true`, renders a table of the current template context (all variables and their values). When debug is `false`, the element is silently stripped from output.
```
<t-debug />
```
Self-closing only.
== Transforms
Applied via `transform="name"` on `<t-var>`, `<t-raw>`, and `<t-let>`. Multiple transforms are pipe-chained: `transform="trim|lower|slugify"`.
=== String transforms
- `upper` --- convert to uppercase
- `lower` --- convert to lowercase
- `capitalize` --- title case (capitalise first letter of each word)
- `trim` --- strip leading and trailing whitespace
- `slugify` --- lowercase, replace non-alphanumeric with hyphens, collapse/strip hyphens
- `truncate:N` --- truncate to N characters (no ellipsis)
- `replace:from:to` --- replace all occurrences of `from` with `to`
- `default:value` --- use `value` if the variable is empty or missing
- `escape` --- HTML-escape (`&`, `<`, `>`, `"`, `'`)
- `url_encode` --- percent-encode for URLs
- `url_decode` --- decode percent-encoded strings
=== Collection transforms
- `join:separator` --- join a newline-separated list with `separator` (default: `, `)
- `split:delimiter` --- split a string on `delimiter` (default: `,`) into newline-separated output
- `first` --- first element (splits on newline or uses separator argument)
- `last` --- last element (splits on newline or uses separator argument)
=== Numeric transforms
- `length` --- string length as a number
- `abs` --- absolute value (best-effort coercion)
- `floor` --- floor/round down (best-effort coercion)
- `ceil` --- ceiling/round up (best-effort coercion)
=== Type-casting transforms
- `int` --- parse as number and truncate to integer
- `float` --- parse as number with minimal representation
- `decimal:N` --- format with exactly N decimal places
- `bool` --- convert to `"true"` or `"false"`
=== Date transforms
- `date:format` --- format ISO date with strftime-style specifiers (`%Y`, `%m`, `%d`, `%e`, `%B`, `%b`, `%H`, `%M`, `%S`, `%%`)
== Errors
| Error | Cause |
| --- | --- |
| `MalformedElement` | Unclosed tag, missing required attribute, unknown transform |
| `TemplateNotFound` | Referenced template not registered in Resolver |
| `CircularReference` | Extend chain cycle or render depth exceeded |
| `DuplicateSlotDefinition` | Same slot defined twice in one include body |
| `UndefinedVariable` | Variable missing with no default body or default transform |
| `OutOfMemory` | Allocator exhausted |