@refrakt-md/runes 0.17.0 → 0.19.0
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/dist/aggregate-resolve.d.ts.map +1 -1
- package/dist/aggregate-resolve.js +69 -0
- package/dist/aggregate-resolve.js.map +1 -1
- package/dist/collection-helpers.d.ts +24 -2
- package/dist/collection-helpers.d.ts.map +1 -1
- package/dist/collection-helpers.js +24 -3
- package/dist/collection-helpers.js.map +1 -1
- package/dist/collection-resolve.d.ts.map +1 -1
- package/dist/collection-resolve.js +15 -8
- package/dist/collection-resolve.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +60 -354
- package/dist/config.js.map +1 -1
- package/dist/drawer-pipeline.d.ts.map +1 -1
- package/dist/drawer-pipeline.js +7 -22
- package/dist/drawer-pipeline.js.map +1 -1
- package/dist/fence-escape.d.ts.map +1 -1
- package/dist/fence-escape.js +7 -1
- package/dist/fence-escape.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/component.d.ts.map +1 -1
- package/dist/lib/component.js +50 -3
- package/dist/lib/component.js.map +1 -1
- package/dist/lib/resolver.d.ts.map +1 -1
- package/dist/lib/resolver.js +13 -2
- package/dist/lib/resolver.js.map +1 -1
- package/dist/tags/aggregate.d.ts.map +1 -1
- package/dist/tags/aggregate.js +10 -3
- package/dist/tags/aggregate.js.map +1 -1
- package/dist/tags/budget.d.ts.map +1 -1
- package/dist/tags/budget.js +99 -9
- package/dist/tags/budget.js.map +1 -1
- package/dist/tags/card.d.ts.map +1 -1
- package/dist/tags/card.js +26 -43
- package/dist/tags/card.js.map +1 -1
- package/dist/tags/chart.d.ts.map +1 -1
- package/dist/tags/chart.js +31 -64
- package/dist/tags/chart.js.map +1 -1
- package/dist/tags/common.d.ts +37 -8
- package/dist/tags/common.d.ts.map +1 -1
- package/dist/tags/common.js +59 -24
- package/dist/tags/common.js.map +1 -1
- package/dist/tags/diagram.d.ts.map +1 -1
- package/dist/tags/diagram.js +33 -7
- package/dist/tags/diagram.js.map +1 -1
- package/dist/tags/embed.d.ts.map +1 -1
- package/dist/tags/embed.js +28 -10
- package/dist/tags/embed.js.map +1 -1
- package/dist/tags/gallery.d.ts.map +1 -1
- package/dist/tags/gallery.js +1 -6
- package/dist/tags/gallery.js.map +1 -1
- package/dist/tags/sandbox.d.ts.map +1 -1
- package/dist/tags/sandbox.js +32 -50
- package/dist/tags/sandbox.js.map +1 -1
- package/package.json +3 -3
package/dist/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isTag,
|
|
1
|
+
import { isTag, readField, resolveGap, ratioToFr, resolveOffset, resolveValign, parsePlacement } from '@refrakt-md/transform';
|
|
2
2
|
import Markdoc from '@markdoc/markdoc';
|
|
3
3
|
const { Tag } = Markdoc;
|
|
4
4
|
import { createComponentRenderable } from './lib/index.js';
|
|
@@ -15,58 +15,6 @@ import { resolveExpands } from './expand-pipeline.js';
|
|
|
15
15
|
import { resolveCollections } from './collection-resolve.js';
|
|
16
16
|
import { resolveRelationships } from './relationships-resolve.js';
|
|
17
17
|
import { resolveAggregates } from './aggregate-resolve.js';
|
|
18
|
-
// ─── Budget postTransform helpers ───
|
|
19
|
-
const BUDGET_CURRENCY_SYMBOLS = {
|
|
20
|
-
USD: '$', EUR: '€', GBP: '£', JPY: '¥', CNY: '¥',
|
|
21
|
-
AUD: 'A$', CAD: 'C$', CHF: 'CHF ', SEK: 'kr', NOK: 'kr', DKK: 'kr',
|
|
22
|
-
INR: '₹', KRW: '₩', BRL: 'R$', MXN: 'MX$', ZAR: 'R',
|
|
23
|
-
};
|
|
24
|
-
function formatBudgetAmount(amount, symbol) {
|
|
25
|
-
const parts = (amount % 1 === 0 ? String(amount) : amount.toFixed(2)).split('.');
|
|
26
|
-
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
27
|
-
return symbol + parts.join('.');
|
|
28
|
-
}
|
|
29
|
-
function parseBudgetDays(duration) {
|
|
30
|
-
let days = 0;
|
|
31
|
-
const dayMatch = duration.match(/(\d+)\s*day/i);
|
|
32
|
-
const weekMatch = duration.match(/(\d+)\s*week/i);
|
|
33
|
-
const monthMatch = duration.match(/(\d+)\s*month/i);
|
|
34
|
-
if (dayMatch)
|
|
35
|
-
days += parseInt(dayMatch[1]);
|
|
36
|
-
if (weekMatch)
|
|
37
|
-
days += parseInt(weekMatch[1]) * 7;
|
|
38
|
-
if (monthMatch)
|
|
39
|
-
days += parseInt(monthMatch[1]) * 30;
|
|
40
|
-
if (days === 0) {
|
|
41
|
-
const num = parseInt(duration);
|
|
42
|
-
if (!isNaN(num))
|
|
43
|
-
days = num;
|
|
44
|
-
}
|
|
45
|
-
return days;
|
|
46
|
-
}
|
|
47
|
-
function parseBudgetAmount(str) {
|
|
48
|
-
const cleaned = str.replace(/[€$£¥₹₩\s]/g, '').replace(/,/g, '');
|
|
49
|
-
const range = cleaned.match(/^([\d.]+)\s*[-–]\s*([\d.]+)/);
|
|
50
|
-
if (range)
|
|
51
|
-
return (parseFloat(range[1]) + parseFloat(range[2])) / 2;
|
|
52
|
-
const num = parseFloat(cleaned);
|
|
53
|
-
return isNaN(num) ? 0 : num;
|
|
54
|
-
}
|
|
55
|
-
/** Recursively find all nodes with a specific data-rune attribute */
|
|
56
|
-
function collectByRune(children, typeName) {
|
|
57
|
-
const results = [];
|
|
58
|
-
for (const c of children) {
|
|
59
|
-
if (isTag(c)) {
|
|
60
|
-
if (c.attributes?.['data-rune'] === typeName) {
|
|
61
|
-
results.push(c);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
results.push(...collectByRune(c.children, typeName));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return results;
|
|
69
|
-
}
|
|
70
18
|
/** Read text content from a property span child */
|
|
71
19
|
function readPropText(node, prop) {
|
|
72
20
|
for (const c of node.children) {
|
|
@@ -93,7 +41,7 @@ export const coreConfig = {
|
|
|
93
41
|
runes: {
|
|
94
42
|
// ─── Simple runes (block name only, engine adds BEM classes) ───
|
|
95
43
|
Accordion: { block: 'accordion', defaultDensity: 'full', sections: { preamble: 'preamble', headline: 'title', blurb: 'description' }, autoLabel: pageSectionAutoLabel, editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' } },
|
|
96
|
-
AccordionItem: { block: 'accordion-item', parent: 'Accordion', rootAttributes: { 'data-state': 'closed' }, autoLabel: { name: 'header' }, editHints: { header: 'inline', body: 'none' } },
|
|
44
|
+
AccordionItem: { block: 'accordion-item', parent: 'Accordion', requiresParent: 'Accordion', rootAttributes: { 'data-state': 'closed' }, autoLabel: { name: 'header' }, editHints: { header: 'inline', body: 'none' } },
|
|
97
45
|
Details: { block: 'details', autoLabel: { summary: 'summary' }, editHints: { summary: 'inline', body: 'none' } },
|
|
98
46
|
Grid: {
|
|
99
47
|
block: 'grid',
|
|
@@ -135,12 +83,13 @@ export const coreConfig = {
|
|
|
135
83
|
},
|
|
136
84
|
layout: { root: ['topbar'] },
|
|
137
85
|
sections: { topbar: 'header' },
|
|
86
|
+
// Opt in to the highlight transform's `theme.code.colorScheme` cascade
|
|
87
|
+
// (topbar + tab chrome flip with the inner code). Static flag → declared
|
|
88
|
+
// via rootAttributes rather than a postTransform. The `data-code-host`
|
|
89
|
+
// consumer reads it truthily, so `"true"` is equivalent to the old
|
|
90
|
+
// valueless boolean.
|
|
91
|
+
rootAttributes: { 'data-code-host': 'true' },
|
|
138
92
|
editHints: { panel: 'code' },
|
|
139
|
-
postTransform(node) {
|
|
140
|
-
// Opt in to the highlight transform's `theme.code.colorScheme`
|
|
141
|
-
// cascade so the topbar + tab chrome flip with the inner code.
|
|
142
|
-
return { ...node, attributes: { ...node.attributes, 'data-code-host': true } };
|
|
143
|
-
},
|
|
144
93
|
},
|
|
145
94
|
PageSection: { block: 'page-section' },
|
|
146
95
|
TableOfContents: { block: 'toc' },
|
|
@@ -188,59 +137,41 @@ export const coreConfig = {
|
|
|
188
137
|
block: 'progress',
|
|
189
138
|
modifiers: { sentiment: { source: 'meta' } },
|
|
190
139
|
},
|
|
191
|
-
/* card — generic content card. The `
|
|
192
|
-
*
|
|
193
|
-
*
|
|
140
|
+
/* card — generic content card. The shared `media-position` modifier
|
|
141
|
+
* places the media zone above, below, or beside the content; `media-ratio`
|
|
142
|
+
* controls media's share of the row in beside layouts. Named parts
|
|
194
143
|
* (media/content/body/footer/link) get rf-card__* from data-name. */
|
|
195
144
|
Card: {
|
|
196
145
|
block: 'card',
|
|
197
|
-
rootAttributes: { 'data-media-position': 'top' },
|
|
198
146
|
modifiers: {
|
|
199
|
-
|
|
147
|
+
'media-position': { source: 'meta', default: 'top', noBemClass: true },
|
|
148
|
+
'media-ratio': { source: 'meta', noBemClass: true },
|
|
149
|
+
valign: { source: 'meta', noBemClass: true },
|
|
150
|
+
collapse: { source: 'meta', noBemClass: true },
|
|
151
|
+
},
|
|
152
|
+
sections: { media: 'media' },
|
|
153
|
+
// SPEC-081/091: the transform emits flat slots; `layout` builds the
|
|
154
|
+
// skeleton — media beside a `content` wrapper grouping eyebrow/body/
|
|
155
|
+
// footer. A base `layout` is the prerequisite for the cover variant
|
|
156
|
+
// ({% ref "SPEC-089" /%}).
|
|
157
|
+
layout: {
|
|
158
|
+
root: ['media', 'content'],
|
|
159
|
+
content: { tag: 'div', children: ['eyebrow', 'body', 'footer'] },
|
|
160
|
+
},
|
|
161
|
+
styles: {
|
|
162
|
+
valign: { prop: '--split-valign', transform: resolveValign },
|
|
200
163
|
},
|
|
201
164
|
},
|
|
202
165
|
Embed: {
|
|
203
166
|
block: 'embed',
|
|
204
167
|
defaultDensity: 'compact',
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const aspect = readMeta(node, 'aspect') || '16:9';
|
|
211
|
-
const provider = readMeta(node, 'provider') || '';
|
|
212
|
-
const [w, h] = aspect.split(':').map(Number);
|
|
213
|
-
const paddingPercent = h && w ? (h / w) * 100 : 56.25;
|
|
214
|
-
// Filter out consumed meta tags
|
|
215
|
-
const contentChildren = node.children.filter(child => {
|
|
216
|
-
if (!isTag(child) || child.name !== 'meta')
|
|
217
|
-
return true;
|
|
218
|
-
const prop = child.attributes['data-field'];
|
|
219
|
-
return !['embedUrl', 'url', 'title', 'aspect', 'provider', 'type'].includes(prop);
|
|
220
|
-
});
|
|
221
|
-
const children = [];
|
|
222
|
-
if (embedUrl) {
|
|
223
|
-
children.push(makeTag('div', { class: `${block}__wrapper`, style: `padding-bottom: ${paddingPercent}%` }, [
|
|
224
|
-
makeTag('iframe', {
|
|
225
|
-
src: embedUrl,
|
|
226
|
-
title,
|
|
227
|
-
frameborder: '0',
|
|
228
|
-
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
|
229
|
-
allowfullscreen: '',
|
|
230
|
-
loading: 'lazy',
|
|
231
|
-
}, []),
|
|
232
|
-
]));
|
|
233
|
-
}
|
|
234
|
-
children.push(makeTag('div', { class: `${block}__fallback` }, contentChildren));
|
|
235
|
-
return {
|
|
236
|
-
...node,
|
|
237
|
-
attributes: {
|
|
238
|
-
...node.attributes,
|
|
239
|
-
...(provider ? { 'data-provider': provider } : {}),
|
|
240
|
-
},
|
|
241
|
-
children,
|
|
242
|
-
};
|
|
168
|
+
// SPEC-081: the rune transform builds the wrapper/iframe/fallback
|
|
169
|
+
// structure directly; `provider` is a bag-only modifier that surfaces
|
|
170
|
+
// as `data-provider`. No postTransform.
|
|
171
|
+
modifiers: {
|
|
172
|
+
provider: { source: 'meta', default: 'generic', noBemClass: true },
|
|
243
173
|
},
|
|
174
|
+
editHints: { fallback: 'none' },
|
|
244
175
|
},
|
|
245
176
|
Breadcrumb: {
|
|
246
177
|
block: 'breadcrumb',
|
|
@@ -253,7 +184,7 @@ export const coreConfig = {
|
|
|
253
184
|
separator: { prop: '--separator', template: '"{}"' },
|
|
254
185
|
},
|
|
255
186
|
},
|
|
256
|
-
BreadcrumbItem: { block: 'breadcrumb-item', parent: 'Breadcrumb' },
|
|
187
|
+
BreadcrumbItem: { block: 'breadcrumb-item', parent: 'Breadcrumb', requiresParent: 'Breadcrumb' },
|
|
257
188
|
Blog: {
|
|
258
189
|
block: 'blog',
|
|
259
190
|
defaultDensity: 'full',
|
|
@@ -290,53 +221,12 @@ export const coreConfig = {
|
|
|
290
221
|
blocks: {
|
|
291
222
|
meta: { fields: ['duration', { field: 'currency', align: 'end' }], layout: 'bar' },
|
|
292
223
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const duration = node.attributes['data-duration'] || '';
|
|
300
|
-
const showPerDay = node.attributes['data-show-per-day'] !== 'false';
|
|
301
|
-
const symbol = BUDGET_CURRENCY_SYMBOLS[currency.toUpperCase()] || currency + ' ';
|
|
302
|
-
// Find all BudgetCategory children and compute totals
|
|
303
|
-
const categories = collectByRune(node.children, 'budget-category');
|
|
304
|
-
let grandTotal = 0;
|
|
305
|
-
for (const cat of categories) {
|
|
306
|
-
// Read from data attributes set by engine from label/subtotal modifiers
|
|
307
|
-
const label = cat.attributes['data-label'] || '';
|
|
308
|
-
const subtotalStr = cat.attributes['data-subtotal'] || '0';
|
|
309
|
-
const subtotal = parseFloat(subtotalStr) || 0;
|
|
310
|
-
grandTotal += subtotal;
|
|
311
|
-
// Inject category header with label and formatted subtotal
|
|
312
|
-
const catHeader = makeTag('div', { class: `${catBlock}__header` }, [
|
|
313
|
-
makeTag('span', { class: `${catBlock}__label` }, [label]),
|
|
314
|
-
makeTag('span', { class: `${catBlock}__subtotal` }, [formatBudgetAmount(subtotal, symbol)]),
|
|
315
|
-
]);
|
|
316
|
-
cat.children.unshift(catHeader);
|
|
317
|
-
}
|
|
318
|
-
// Build footer with totals
|
|
319
|
-
const footerChildren = [
|
|
320
|
-
makeTag('div', { class: `${block}__total` }, [
|
|
321
|
-
makeTag('span', { class: `${block}__total-label` }, ['Total']),
|
|
322
|
-
makeTag('span', { class: `${block}__total-amount` }, [formatBudgetAmount(grandTotal, symbol)]),
|
|
323
|
-
]),
|
|
324
|
-
];
|
|
325
|
-
if (duration && showPerDay) {
|
|
326
|
-
const days = parseBudgetDays(duration);
|
|
327
|
-
if (days > 0) {
|
|
328
|
-
const perDay = grandTotal / days;
|
|
329
|
-
footerChildren.push(makeTag('div', { class: `${block}__per-day` }, [
|
|
330
|
-
makeTag('span', { class: `${block}__per-day-label` }, ['Per day']),
|
|
331
|
-
makeTag('span', { class: `${block}__per-day-amount` }, [formatBudgetAmount(perDay, symbol)]),
|
|
332
|
-
]));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
const footer = makeTag('div', { class: `${block}__footer` }, footerChildren);
|
|
336
|
-
return {
|
|
337
|
-
...node,
|
|
338
|
-
children: [...node.children, footer],
|
|
339
|
-
};
|
|
224
|
+
// SPEC-081: the transform emits flat header slots and derives the
|
|
225
|
+
// totals (footer + category headers built there); `layout` builds the
|
|
226
|
+
// preamble <header>, and the categories / footer append after it.
|
|
227
|
+
layout: {
|
|
228
|
+
root: ['meta', 'preamble'],
|
|
229
|
+
preamble: { tag: 'header', children: ['headline', 'blurb', 'image'] },
|
|
340
230
|
},
|
|
341
231
|
},
|
|
342
232
|
BudgetCategory: {
|
|
@@ -396,12 +286,10 @@ export const coreConfig = {
|
|
|
396
286
|
modifiers: {
|
|
397
287
|
layout: { source: 'meta', default: 'grid' },
|
|
398
288
|
lightbox: { source: 'meta', default: 'true', noBemClass: true },
|
|
399
|
-
gap: { source: 'meta', default: 'default', noBemClass: true },
|
|
400
289
|
columns: { source: 'meta', noBemClass: true },
|
|
401
290
|
},
|
|
402
291
|
styles: {
|
|
403
292
|
columns: '--gallery-columns',
|
|
404
|
-
gap: { prop: '--gallery-gap', transform: resolveGap },
|
|
405
293
|
},
|
|
406
294
|
editHints: { items: 'none' },
|
|
407
295
|
},
|
|
@@ -493,115 +381,14 @@ export const coreConfig = {
|
|
|
493
381
|
Chart: {
|
|
494
382
|
block: 'chart',
|
|
495
383
|
defaultDensity: 'compact',
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
let chartData = { headers: [], rows: [] };
|
|
503
|
-
try {
|
|
504
|
-
chartData = JSON.parse(dataJson);
|
|
505
|
-
}
|
|
506
|
-
catch { /* fallback */ }
|
|
507
|
-
const colors = [
|
|
508
|
-
'var(--rf-color-info)', 'var(--rf-color-success)',
|
|
509
|
-
'var(--rf-color-warning)', 'var(--rf-color-danger)',
|
|
510
|
-
'#7c3aed', '#0891b2',
|
|
511
|
-
];
|
|
512
|
-
const svgW = 600, svgH = 300;
|
|
513
|
-
const pad = { top: 30, right: 20, bottom: 40, left: 50 };
|
|
514
|
-
const cw = svgW - pad.left - pad.right;
|
|
515
|
-
const ch = svgH - pad.top - pad.bottom;
|
|
516
|
-
const labels = chartData.rows.map(r => r[0] || '');
|
|
517
|
-
const series = chartData.headers.slice(1);
|
|
518
|
-
const values = chartData.rows.map(r => r.slice(1).map(v => parseFloat(v) || 0));
|
|
519
|
-
const maxVal = Math.max(...values.flat(), 1);
|
|
520
|
-
const bgw = cw / Math.max(labels.length, 1);
|
|
521
|
-
const bw = bgw / Math.max(series.length + 1, 2);
|
|
522
|
-
// Build SVG children
|
|
523
|
-
const svgChildren = [];
|
|
524
|
-
// Axes
|
|
525
|
-
svgChildren.push(makeTag('line', {
|
|
526
|
-
x1: String(pad.left), y1: String(pad.top),
|
|
527
|
-
x2: String(pad.left), y2: String(svgH - pad.bottom),
|
|
528
|
-
stroke: 'var(--rf-color-border)', 'stroke-width': '1',
|
|
529
|
-
}, []));
|
|
530
|
-
svgChildren.push(makeTag('line', {
|
|
531
|
-
x1: String(pad.left), y1: String(svgH - pad.bottom),
|
|
532
|
-
x2: String(svgW - pad.right), y2: String(svgH - pad.bottom),
|
|
533
|
-
stroke: 'var(--rf-color-border)', 'stroke-width': '1',
|
|
534
|
-
}, []));
|
|
535
|
-
if (chartType === 'bar') {
|
|
536
|
-
for (let i = 0; i < labels.length; i++) {
|
|
537
|
-
for (let si = 0; si < series.length; si++) {
|
|
538
|
-
const h = (values[i][si] / maxVal) * ch;
|
|
539
|
-
svgChildren.push(makeTag('rect', {
|
|
540
|
-
x: String(pad.left + i * bgw + si * bw + bw * 0.25),
|
|
541
|
-
y: String(pad.top + ch - h),
|
|
542
|
-
width: String(bw * 0.75),
|
|
543
|
-
height: String(h),
|
|
544
|
-
style: `fill: ${colors[si % colors.length]}`,
|
|
545
|
-
rx: '2',
|
|
546
|
-
}, []));
|
|
547
|
-
}
|
|
548
|
-
svgChildren.push(makeTag('text', {
|
|
549
|
-
x: String(pad.left + i * bgw + bgw / 2),
|
|
550
|
-
y: String(svgH - pad.bottom + 20),
|
|
551
|
-
'text-anchor': 'middle', 'font-size': '12',
|
|
552
|
-
fill: 'var(--rf-color-muted)',
|
|
553
|
-
}, [labels[i]]));
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
else if (chartType === 'line') {
|
|
557
|
-
for (let si = 0; si < series.length; si++) {
|
|
558
|
-
const pts = labels.map((_, i) => `${pad.left + i * bgw + bgw / 2},${pad.top + ch - (values[i][si] / maxVal) * ch}`).join(' ');
|
|
559
|
-
svgChildren.push(makeTag('polyline', {
|
|
560
|
-
points: pts, fill: 'none',
|
|
561
|
-
style: `stroke: ${colors[si % colors.length]}`,
|
|
562
|
-
'stroke-width': '2',
|
|
563
|
-
}, []));
|
|
564
|
-
for (let i = 0; i < labels.length; i++) {
|
|
565
|
-
svgChildren.push(makeTag('circle', {
|
|
566
|
-
cx: String(pad.left + i * bgw + bgw / 2),
|
|
567
|
-
cy: String(pad.top + ch - (values[i][si] / maxVal) * ch),
|
|
568
|
-
r: '4',
|
|
569
|
-
style: `fill: ${colors[si % colors.length]}`,
|
|
570
|
-
}, []));
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
for (let i = 0; i < labels.length; i++) {
|
|
574
|
-
svgChildren.push(makeTag('text', {
|
|
575
|
-
x: String(pad.left + i * bgw + bgw / 2),
|
|
576
|
-
y: String(svgH - pad.bottom + 20),
|
|
577
|
-
'text-anchor': 'middle', 'font-size': '12',
|
|
578
|
-
fill: 'var(--rf-color-muted)',
|
|
579
|
-
}, [labels[i]]));
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
const children = [];
|
|
583
|
-
if (title) {
|
|
584
|
-
children.push(makeTag('figcaption', { class: `${block}__title` }, [title]));
|
|
585
|
-
}
|
|
586
|
-
children.push(makeTag('div', { class: `${block}__container` }, [
|
|
587
|
-
makeTag('svg', {
|
|
588
|
-
viewBox: `0 0 ${svgW} ${svgH}`,
|
|
589
|
-
class: `${block}__svg`,
|
|
590
|
-
}, svgChildren),
|
|
591
|
-
]));
|
|
592
|
-
// Legend
|
|
593
|
-
if (series.length > 1) {
|
|
594
|
-
const legendItems = series.map((name, i) => makeTag('span', { class: `${block}__legend-item` }, [
|
|
595
|
-
makeTag('span', {
|
|
596
|
-
class: `${block}__legend-color`,
|
|
597
|
-
style: `background: ${colors[i % colors.length]};`,
|
|
598
|
-
}, []),
|
|
599
|
-
name,
|
|
600
|
-
]));
|
|
601
|
-
children.push(makeTag('div', { class: `${block}__legend` }, legendItems));
|
|
602
|
-
}
|
|
603
|
-
return { ...node, children };
|
|
384
|
+
// SPEC-083: the transform emits the rf-chart element wrapping the data
|
|
385
|
+
// `<table>`; `type` / `stacked` are bag-only modifiers (→ data-type /
|
|
386
|
+
// data-stacked) the web component reads. No postTransform.
|
|
387
|
+
modifiers: {
|
|
388
|
+
type: { source: 'meta', default: 'bar', noBemClass: true },
|
|
389
|
+
stacked: { source: 'meta', noBemClass: true },
|
|
604
390
|
},
|
|
391
|
+
editHints: { data: 'none' },
|
|
605
392
|
},
|
|
606
393
|
// ─── Text formatting & layout runes ───
|
|
607
394
|
PullQuote: {
|
|
@@ -669,8 +456,8 @@ export const coreConfig = {
|
|
|
669
456
|
},
|
|
670
457
|
// ─── Interactive runes (still get BEM classes, components add behavior) ───
|
|
671
458
|
TabGroup: { block: 'tabs', defaultDensity: 'full', sections: { preamble: 'preamble', headline: 'title', blurb: 'description' }, autoLabel: pageSectionAutoLabel, editHints: { headline: 'inline', eyebrow: 'inline', blurb: 'inline' } },
|
|
672
|
-
Tab: { block: 'tab', parent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' }, editHints: { name: 'inline' } },
|
|
673
|
-
TabPanel: { block: 'tab-panel', parent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' } },
|
|
459
|
+
Tab: { block: 'tab', parent: 'TabGroup', requiresParent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' }, editHints: { name: 'inline' } },
|
|
460
|
+
TabPanel: { block: 'tab-panel', parent: 'TabGroup', requiresParent: 'TabGroup', rootAttributes: { 'data-state': 'inactive' } },
|
|
674
461
|
DataTable: {
|
|
675
462
|
block: 'datatable',
|
|
676
463
|
defaultDensity: 'compact',
|
|
@@ -730,36 +517,15 @@ export const coreConfig = {
|
|
|
730
517
|
},
|
|
731
518
|
editHints: { panels: 'none' },
|
|
732
519
|
},
|
|
733
|
-
JuxtaposePanel: { block: 'juxtapose-panel', parent: 'Juxtapose', rootAttributes: { 'data-state': 'inactive' }, editHints: { body: 'none' } },
|
|
520
|
+
JuxtaposePanel: { block: 'juxtapose-panel', parent: 'Juxtapose', requiresParent: 'Juxtapose', rootAttributes: { 'data-state': 'inactive' }, editHints: { body: 'none' } },
|
|
734
521
|
Diagram: {
|
|
735
522
|
block: 'diagram',
|
|
736
523
|
defaultDensity: 'compact',
|
|
737
524
|
editHints: { source: 'code' },
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const sourceMeta = findByDataName(node, 'source');
|
|
743
|
-
const source = sourceMeta?.attributes?.content || '';
|
|
744
|
-
// Build fallback HTML (visible in SSR, replaced by web component)
|
|
745
|
-
const children = [];
|
|
746
|
-
if (title) {
|
|
747
|
-
children.push(makeTag('figcaption', { class: `${block}__title` }, [title]));
|
|
748
|
-
}
|
|
749
|
-
const containerChildren = source
|
|
750
|
-
? [makeTag('pre', { class: `${block}__source` }, [makeTag('code', {}, [source])])]
|
|
751
|
-
: [];
|
|
752
|
-
children.push(makeTag('div', { class: `${block}__container` }, containerChildren));
|
|
753
|
-
// Hidden source for web component to read
|
|
754
|
-
if (source) {
|
|
755
|
-
children.push(makeTag('div', { 'data-content': 'source', style: 'display:none' }, [source]));
|
|
756
|
-
}
|
|
757
|
-
return {
|
|
758
|
-
...node,
|
|
759
|
-
name: 'rf-diagram',
|
|
760
|
-
attributes: { ...node.attributes, 'data-language': language },
|
|
761
|
-
children,
|
|
762
|
-
};
|
|
525
|
+
// SPEC-081: the rune transform emits the `rf-diagram` element + SSR
|
|
526
|
+
// fallback; `language` is a bag-only modifier (→ data-language).
|
|
527
|
+
modifiers: {
|
|
528
|
+
language: { source: 'meta', default: 'mermaid', noBemClass: true },
|
|
763
529
|
},
|
|
764
530
|
},
|
|
765
531
|
Tint: { block: 'tint', parent: '*' },
|
|
@@ -769,64 +535,6 @@ export const coreConfig = {
|
|
|
769
535
|
block: 'sandbox',
|
|
770
536
|
defaultDensity: 'compact',
|
|
771
537
|
editHints: { source: 'code' },
|
|
772
|
-
postTransform(node) {
|
|
773
|
-
// Read meta values
|
|
774
|
-
const content = readMeta(node, 'content') || '';
|
|
775
|
-
const framework = readMeta(node, 'framework') || '';
|
|
776
|
-
const dependencies = readMeta(node, 'dependencies') || '';
|
|
777
|
-
const label = readMeta(node, 'label') || '';
|
|
778
|
-
const height = readMeta(node, 'height') || 'auto';
|
|
779
|
-
const designTokens = readMeta(node, 'design-tokens') || '';
|
|
780
|
-
const securityMode = readMeta(node, 'security-mode') || 'trusted';
|
|
781
|
-
const allowJs = readMeta(node, 'allow-js') || 'true';
|
|
782
|
-
const sandboxOrigin = readMeta(node, 'sandbox-origin') || '';
|
|
783
|
-
// Keep non-meta children (fallback pre) and extract source panels
|
|
784
|
-
const fallbackChildren = [];
|
|
785
|
-
const sourcePanelOrigins = [];
|
|
786
|
-
for (const child of node.children) {
|
|
787
|
-
if (!isTag(child)) {
|
|
788
|
-
fallbackChildren.push(child);
|
|
789
|
-
continue;
|
|
790
|
-
}
|
|
791
|
-
if (child.name === 'meta') {
|
|
792
|
-
// Collect origin data from source panels
|
|
793
|
-
if (child.attributes?.['data-field'] === 'source-panel' && child.attributes?.['data-origin']) {
|
|
794
|
-
sourcePanelOrigins.push(`${child.attributes['data-label'] || ''}\t${child.attributes['data-origin']}`);
|
|
795
|
-
}
|
|
796
|
-
continue;
|
|
797
|
-
}
|
|
798
|
-
fallbackChildren.push(child);
|
|
799
|
-
}
|
|
800
|
-
// Wrap fallback and source in <template> tags (inert/invisible).
|
|
801
|
-
// Using <template> instead of <div> avoids HTML parser issues:
|
|
802
|
-
// when <rf-sandbox> is inside <p>, block elements like <pre> or
|
|
803
|
-
// <div> cause <p> to auto-close, pushing children out of the
|
|
804
|
-
// custom element. <template> is parsed but never rendered.
|
|
805
|
-
const children = [
|
|
806
|
-
...(fallbackChildren.length > 0
|
|
807
|
-
? [makeTag('template', { 'data-content': 'fallback' }, fallbackChildren)]
|
|
808
|
-
: []),
|
|
809
|
-
makeTag('template', { 'data-content': 'source' }, [content]),
|
|
810
|
-
];
|
|
811
|
-
return {
|
|
812
|
-
...node,
|
|
813
|
-
name: 'rf-sandbox',
|
|
814
|
-
attributes: {
|
|
815
|
-
...node.attributes,
|
|
816
|
-
'data-source-content': content,
|
|
817
|
-
...(framework ? { 'data-framework': framework } : {}),
|
|
818
|
-
...(dependencies ? { 'data-dependencies': dependencies } : {}),
|
|
819
|
-
...(label ? { 'data-label': label } : {}),
|
|
820
|
-
'data-height': height,
|
|
821
|
-
...(designTokens ? { 'data-design-tokens': designTokens } : {}),
|
|
822
|
-
...(sourcePanelOrigins.length > 0 ? { 'data-source-origins': sourcePanelOrigins.join('\n') } : {}),
|
|
823
|
-
'data-security-mode': securityMode,
|
|
824
|
-
'data-allow-js': allowJs,
|
|
825
|
-
...(sandboxOrigin ? { 'data-sandbox-origin': sandboxOrigin } : {}),
|
|
826
|
-
},
|
|
827
|
-
children,
|
|
828
|
-
};
|
|
829
|
-
},
|
|
830
538
|
},
|
|
831
539
|
},
|
|
832
540
|
};
|
|
@@ -2007,14 +1715,12 @@ function resolveBlogPosts(renderable, allPosts, ctx, pageUrl) {
|
|
|
2007
1715
|
const result = mapBlogTags(renderable, (tag) => {
|
|
2008
1716
|
if (tag.attributes['data-rune'] !== 'blog')
|
|
2009
1717
|
return tag;
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
const
|
|
2013
|
-
const
|
|
2014
|
-
const
|
|
2015
|
-
const
|
|
2016
|
-
const filterStr = Tag.isTag(filterMeta) ? filterMeta.attributes.content : '';
|
|
2017
|
-
const limitStr = Tag.isTag(limitMeta) ? limitMeta.attributes.content : '';
|
|
1718
|
+
// SPEC-082: read field values from the bag (bag-first, meta-fallback).
|
|
1719
|
+
// The cross-page tree still carries the `data-rune-fields` attribute.
|
|
1720
|
+
const folder = readField(tag, 'folder') ?? '';
|
|
1721
|
+
const sort = readField(tag, 'sort') || 'date-desc';
|
|
1722
|
+
const filterStr = readField(tag, 'filter') ?? '';
|
|
1723
|
+
const limitStr = readField(tag, 'limit') ?? '';
|
|
2018
1724
|
const limit = limitStr ? parseInt(limitStr, 10) : undefined;
|
|
2019
1725
|
if (!folder) {
|
|
2020
1726
|
ctx.warn('Blog rune missing folder attribute', pageUrl);
|