boxwood 2.18.0 → 2.18.1
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/package.json +1 -1
- package/ui/prose/README.md +11 -0
- package/ui/prose/utilities/validate.js +2 -14
- package/ui/utilities/parseCustomTag.js +22 -10
package/package.json
CHANGED
package/ui/prose/README.md
CHANGED
|
@@ -175,6 +175,17 @@ literals:
|
|
|
175
175
|
<Hero sources="{[cover ?? 'default.jpg', banner]}" />
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
+
## Attribute Interpolation
|
|
179
|
+
|
|
180
|
+
An attribute whose entire value is a single `{expression}` resolves to the
|
|
181
|
+
raw value (array, object, number) - that is what makes
|
|
182
|
+
`images="{images.slice(0, 2)}"` work. Expressions can also be embedded in a
|
|
183
|
+
longer attribute string, which always produces a string:
|
|
184
|
+
|
|
185
|
+
```markdown
|
|
186
|
+
<Card href="/products/{id}" alt="Photo of {name ?? 'someone'}" />
|
|
187
|
+
```
|
|
188
|
+
|
|
178
189
|
## Conditional Rendering
|
|
179
190
|
|
|
180
191
|
Use `{#if}` blocks to conditionally render content based on data:
|
|
@@ -17,7 +17,6 @@ const CONDITION_REGEXP = /^(.+?)\s*(>=|<=|>|<|==|!=)\s*(.+)$/
|
|
|
17
17
|
const INLINE_CODE_REGEXP = /(`+)([^`]+)\1/g
|
|
18
18
|
const BRACE_REGEXP = /\{([^{}]*)\}/g
|
|
19
19
|
const COMPONENT_TAG_REGEXP = /^<\/?([A-Z][A-Za-z0-9-]*)/
|
|
20
|
-
const ATTRIBUTE_EXPRESSION_REGEXP = /=\s*(?:"\{([^}"]*)\}"|'\{([^}']*)\}'|\{([^}]*)\})/g
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* Validate a Prose template and report problems that the renderer
|
|
@@ -253,19 +252,8 @@ function validate(text, options = {}) {
|
|
|
253
252
|
)
|
|
254
253
|
}
|
|
255
254
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let attrMatch
|
|
259
|
-
while ((attrMatch = ATTRIBUTE_EXPRESSION_REGEXP.exec(trimmed)) !== null) {
|
|
260
|
-
const expression = attrMatch[1] ?? attrMatch[2] ?? attrMatch[3]
|
|
261
|
-
if (expression && expression.trim()) {
|
|
262
|
-
validateExpression(expression, lineNumber)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
continue
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Braced tags and expressions
|
|
255
|
+
// Braced tags and expressions - on component lines this also covers
|
|
256
|
+
// attribute expressions, both whole-value ({expr}) and partial (/p/{id})
|
|
269
257
|
let braceMatch
|
|
270
258
|
while ((braceMatch = BRACE_REGEXP.exec(line)) !== null) {
|
|
271
259
|
// Escaped braces are literal
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
resolveExpression,
|
|
3
|
+
replaceVariables,
|
|
4
|
+
} = require("./replaceVariables")
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Parse a custom component tag from markdown
|
|
@@ -160,18 +163,24 @@ function parseAttributes(attributesStr) {
|
|
|
160
163
|
}
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
// Whole-value variable reference: "{variable}" - resolves to the raw
|
|
167
|
+
// value (array, object, number), not a string
|
|
168
|
+
const inner = value.substring(1, value.length - 1)
|
|
169
|
+
if (
|
|
170
|
+
value.startsWith("{") &&
|
|
171
|
+
value.endsWith("}") &&
|
|
172
|
+
inner.trim() &&
|
|
173
|
+
!inner.includes("{") &&
|
|
174
|
+
!inner.includes("}")
|
|
175
|
+
) {
|
|
176
|
+
attributes[name] = { __variable: inner.trim() }
|
|
177
|
+
} else if (/\{[^{}]+\}/.test(value)) {
|
|
178
|
+
// Partial interpolation: "/products/{id}" or "Photo of {name}"
|
|
179
|
+
attributes[name] = { __interpolate: value }
|
|
171
180
|
} else {
|
|
172
181
|
attributes[name] = value
|
|
173
182
|
}
|
|
174
|
-
|
|
183
|
+
|
|
175
184
|
if (i < str.length) i++ // Skip closing quote
|
|
176
185
|
} else if (str[i] === "{") {
|
|
177
186
|
// Variable reference
|
|
@@ -216,6 +225,9 @@ function resolveAttributes(attributes, data) {
|
|
|
216
225
|
const variablePath = value.__variable
|
|
217
226
|
const resolvedValue = resolveExpression(data, variablePath)
|
|
218
227
|
resolved[key] = resolvedValue
|
|
228
|
+
} else if (value && typeof value === "object" && value.__interpolate) {
|
|
229
|
+
// Partial interpolation always produces a string
|
|
230
|
+
resolved[key] = replaceVariables(value.__interpolate, data)
|
|
219
231
|
} else {
|
|
220
232
|
resolved[key] = value
|
|
221
233
|
}
|