@rettangoli/sites 0.1.1 → 0.2.0-rc2

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.
@@ -0,0 +1,350 @@
1
+ import { convertToHtml } from 'yahtml';
2
+ import { parseAndRender } from 'jempl';
3
+ import path from 'path';
4
+ import yaml from 'js-yaml';
5
+
6
+ import MarkdownIt from 'markdown-it';
7
+
8
+ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {} }) {
9
+ return function build() {
10
+ // Use provided mdRender or default to standard markdown-it
11
+ const md = mdRender || MarkdownIt();
12
+
13
+ // Read all partials and create a JSON object
14
+ const partialsDir = path.join(rootDir, 'partials');
15
+ const partials = {};
16
+
17
+ if (fs.existsSync(partialsDir)) {
18
+ const files = fs.readdirSync(partialsDir);
19
+ files.forEach(file => {
20
+ const filePath = path.join(partialsDir, file);
21
+ const fileContent = fs.readFileSync(filePath, 'utf8');
22
+ const nameWithoutExt = path.basename(file, path.extname(file));
23
+ // Convert partial content from YAML string to JSON
24
+ partials[nameWithoutExt] = yaml.load(fileContent, { schema: yaml.JSON_SCHEMA });
25
+ });
26
+ }
27
+
28
+ // Read all data files and create a JSON object
29
+ const dataDir = path.join(rootDir, 'data');
30
+ const globalData = {};
31
+
32
+ if (fs.existsSync(dataDir)) {
33
+ const files = fs.readdirSync(dataDir);
34
+ files.forEach(file => {
35
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
36
+ const filePath = path.join(dataDir, file);
37
+ const fileContent = fs.readFileSync(filePath, 'utf8');
38
+ const nameWithoutExt = path.basename(file, path.extname(file));
39
+ // Load YAML content and store under filename key
40
+ globalData[nameWithoutExt] = yaml.load(fileContent, { schema: yaml.JSON_SCHEMA });
41
+ }
42
+ });
43
+ }
44
+
45
+ // Read all templates and create a JSON object
46
+ const templatesDir = path.join(rootDir, 'templates');
47
+ const templates = {};
48
+
49
+ function readTemplatesRecursively(dir, basePath = '') {
50
+ if (!fs.existsSync(dir)) return;
51
+
52
+ const items = fs.readdirSync(dir, { withFileTypes: true });
53
+
54
+ items.forEach(item => {
55
+ const itemPath = path.join(dir, item.name);
56
+
57
+ if (item.isDirectory()) {
58
+ // Recursively read subdirectories
59
+ const newBasePath = basePath ? `${basePath}/${item.name}` : item.name;
60
+ readTemplatesRecursively(itemPath, newBasePath);
61
+ } else if (item.isFile() && item.name.endsWith('.yaml')) {
62
+ // Read and convert YAML file
63
+ const fileContent = fs.readFileSync(itemPath, 'utf8');
64
+ const nameWithoutExt = path.basename(item.name, '.yaml');
65
+ const templateKey = basePath ? `${basePath}/${nameWithoutExt}` : nameWithoutExt;
66
+ templates[templateKey] = yaml.load(fileContent, { schema: yaml.JSON_SCHEMA });
67
+ }
68
+ });
69
+ }
70
+
71
+ readTemplatesRecursively(templatesDir);
72
+
73
+ // Function to extract frontmatter from a page file
74
+ function extractFrontmatter(pagePath) {
75
+ const pageFileContent = fs.readFileSync(pagePath, 'utf8');
76
+ const lines = pageFileContent.split('\n');
77
+ let frontmatterStart = -1;
78
+ let frontmatterEnd = -1;
79
+ let frontmatterCount = 0;
80
+
81
+ for (let i = 0; i < lines.length; i++) {
82
+ if (lines[i].trim() === '---') {
83
+ frontmatterCount++;
84
+ if (frontmatterCount === 1) {
85
+ frontmatterStart = i + 1;
86
+ } else if (frontmatterCount === 2) {
87
+ frontmatterEnd = i;
88
+ break;
89
+ }
90
+ }
91
+ }
92
+
93
+ let frontmatter = {};
94
+ if (frontmatterStart > 0 && frontmatterEnd > frontmatterStart) {
95
+ const frontmatterContent = lines.slice(frontmatterStart, frontmatterEnd).join('\n');
96
+ frontmatter = yaml.load(frontmatterContent, { schema: yaml.JSON_SCHEMA }) || {};
97
+ }
98
+
99
+ return frontmatter;
100
+ }
101
+
102
+ // Function to scan all pages and build collections
103
+ function buildCollections() {
104
+ const collections = {};
105
+ const pagesDir = path.join(rootDir, 'pages');
106
+
107
+ function scanPages(dir, basePath = '') {
108
+ const fullDir = path.join(pagesDir, basePath);
109
+ if (!fs.existsSync(fullDir)) return;
110
+
111
+ const items = fs.readdirSync(fullDir, { withFileTypes: true });
112
+
113
+ for (const item of items) {
114
+ const itemPath = path.join(fullDir, item.name);
115
+ const relativePath = basePath ? path.join(basePath, item.name) : item.name;
116
+
117
+ if (item.isDirectory()) {
118
+ // Recursively scan subdirectories
119
+ scanPages(dir, relativePath);
120
+ } else if (item.isFile() && (item.name.endsWith('.yaml') || item.name.endsWith('.md'))) {
121
+ // Extract frontmatter
122
+ const frontmatter = extractFrontmatter(itemPath);
123
+
124
+ // Calculate URL
125
+ const outputFileName = item.name.replace(/\.(yaml|md)$/, '.html');
126
+ const outputRelativePath = basePath ? path.join(basePath, outputFileName) : outputFileName;
127
+ const url = '/' + outputRelativePath.replace(/\\/g, '/');
128
+
129
+ // Process tags
130
+ if (frontmatter.tags) {
131
+ // Normalize tags to array
132
+ const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [frontmatter.tags];
133
+
134
+ // Add to collections
135
+ tags.forEach(tag => {
136
+ if (typeof tag === 'string' && tag.trim()) {
137
+ const trimmedTag = tag.trim();
138
+ if (!collections[trimmedTag]) {
139
+ collections[trimmedTag] = [];
140
+ }
141
+ collections[trimmedTag].push({
142
+ data: frontmatter,
143
+ url: url
144
+ });
145
+ }
146
+ });
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ scanPages('');
153
+ return collections;
154
+ }
155
+
156
+ // Build collections in first pass
157
+ console.log('Building collections...');
158
+ const collections = buildCollections();
159
+
160
+ // Function to process a single page file
161
+ function processPage(pagePath, outputRelativePath, isMarkdown = false) {
162
+ console.log(`Processing ${pagePath}...`);
163
+
164
+ // Read page content
165
+ const pageFileContent = fs.readFileSync(pagePath, 'utf8');
166
+
167
+ // Extract frontmatter and content
168
+ const lines = pageFileContent.split('\n');
169
+ let frontmatterStart = -1;
170
+ let frontmatterEnd = -1;
171
+ let frontmatterCount = 0;
172
+
173
+ for (let i = 0; i < lines.length; i++) {
174
+ if (lines[i].trim() === '---') {
175
+ frontmatterCount++;
176
+ if (frontmatterCount === 1) {
177
+ frontmatterStart = i + 1;
178
+ } else if (frontmatterCount === 2) {
179
+ frontmatterEnd = i;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+
185
+ // Store frontmatter
186
+ let frontmatter = {};
187
+ if (frontmatterStart > 0 && frontmatterEnd > frontmatterStart) {
188
+ const frontmatterContent = lines.slice(frontmatterStart, frontmatterEnd).join('\n');
189
+ frontmatter = yaml.load(frontmatterContent, { schema: yaml.JSON_SCHEMA }) || {};
190
+ }
191
+
192
+ // Get content after frontmatter
193
+ const contentStart = frontmatterEnd + 1;
194
+ const rawContent = lines.slice(contentStart).join('\n').trim();
195
+
196
+ // Merge global data with frontmatter and collections for the page context
197
+ const pageData = { ...globalData, ...frontmatter, collections };
198
+
199
+ let processedPageContent;
200
+
201
+ if (isMarkdown) {
202
+ // Process markdown content with MarkdownIt
203
+ const htmlContent = md.render(rawContent);
204
+ // For markdown, store as raw HTML that will be inserted directly
205
+ processedPageContent = { __html: htmlContent };
206
+ } else {
207
+ // Convert YAML content to JSON
208
+ const pageContent = yaml.load(rawContent, { schema: yaml.JSON_SCHEMA });
209
+ // Process the page content to resolve any $partial references with page data
210
+ processedPageContent = parseAndRender(pageContent, pageData, { partials, functions });
211
+ }
212
+
213
+ // Find the template specified in frontmatter
214
+ let templateToUse = null;
215
+ if (frontmatter.template) {
216
+ // Look up template by exact path
217
+ templateToUse = templates[frontmatter.template];
218
+ if (!templateToUse) {
219
+ throw new Error(`Template "${frontmatter.template}" not found in ${pagePath}. Available templates: ${Object.keys(templates).join(', ')}`);
220
+ }
221
+ }
222
+
223
+ // Use the template with jempl to render the processed page content
224
+ let htmlString;
225
+
226
+ if (isMarkdown) {
227
+ if (templateToUse) {
228
+ // For markdown with template, use a placeholder and replace after
229
+ const placeholder = '___MARKDOWN_CONTENT_PLACEHOLDER___';
230
+ const templateData = { ...pageData, content: placeholder, collections };
231
+ const templateResult = parseAndRender(templateToUse, templateData, { partials, functions });
232
+ htmlString = convertToHtml(templateResult);
233
+ // Replace the placeholder with actual HTML content
234
+ htmlString = htmlString.replace(placeholder, processedPageContent.__html);
235
+ } else {
236
+ // Markdown without template - use HTML directly
237
+ htmlString = processedPageContent.__html;
238
+ }
239
+ } else {
240
+ // YAML content
241
+ const templateData = { ...pageData, content: processedPageContent, collections };
242
+ const result = templateToUse
243
+ ? parseAndRender(templateToUse, templateData, { partials, functions })
244
+ : processedPageContent;
245
+ // Ensure result is an array for convertToHtml
246
+ const resultArray = Array.isArray(result) ? result : [result];
247
+ htmlString = convertToHtml(resultArray);
248
+ }
249
+
250
+ // Create output directory if it doesn't exist
251
+ const outputPath = path.join(rootDir, '_site', outputRelativePath);
252
+ const outputDir = path.dirname(outputPath);
253
+ if (!fs.existsSync(outputDir)) {
254
+ fs.mkdirSync(outputDir, { recursive: true });
255
+ }
256
+
257
+ // Write HTML to output file
258
+ fs.writeFileSync(outputPath, htmlString);
259
+ console.log(` -> Written to ${outputPath}`);
260
+ }
261
+
262
+ // Process all YAML and Markdown files in pages directory recursively
263
+ function processAllPages(dir, basePath = '') {
264
+ const pagesDir = path.join(rootDir, 'pages');
265
+ const fullDir = path.join(pagesDir, basePath);
266
+
267
+ if (!fs.existsSync(fullDir)) return;
268
+
269
+ const items = fs.readdirSync(fullDir, { withFileTypes: true });
270
+
271
+ for (const item of items) {
272
+ const itemPath = path.join(fullDir, item.name);
273
+ const relativePath = basePath ? path.join(basePath, item.name) : item.name;
274
+
275
+ if (item.isDirectory()) {
276
+ // Recursively process subdirectories
277
+ processAllPages(dir, relativePath);
278
+ } else if (item.isFile()) {
279
+ if (item.name.endsWith('.yaml')) {
280
+ // Process YAML file
281
+ const outputFileName = item.name.replace('.yaml', '.html');
282
+ const outputRelativePath = basePath ? path.join(basePath, outputFileName) : outputFileName;
283
+ processPage(itemPath, outputRelativePath, false);
284
+ } else if (item.name.endsWith('.md')) {
285
+ // Process Markdown file
286
+ const outputFileName = item.name.replace('.md', '.html');
287
+ const outputRelativePath = basePath ? path.join(basePath, outputFileName) : outputFileName;
288
+ processPage(itemPath, outputRelativePath, true);
289
+ }
290
+ // Ignore other file types
291
+ }
292
+ }
293
+ }
294
+
295
+ // Function to copy static files recursively
296
+ function copyStaticFiles() {
297
+ const staticDir = path.join(rootDir, 'static');
298
+ const outputDir = path.join(rootDir, '_site');
299
+
300
+ if (!fs.existsSync(staticDir)) {
301
+ return;
302
+ }
303
+
304
+ // Ensure output directory exists
305
+ if (!fs.existsSync(outputDir)) {
306
+ fs.mkdirSync(outputDir, { recursive: true });
307
+ }
308
+
309
+ function copyRecursive(src, dest) {
310
+ const stats = fs.statSync(src);
311
+
312
+ if (stats.isDirectory()) {
313
+ // Create directory if it doesn't exist
314
+ if (!fs.existsSync(dest)) {
315
+ fs.mkdirSync(dest, { recursive: true });
316
+ }
317
+
318
+ // Copy all items in directory
319
+ const items = fs.readdirSync(src);
320
+ items.forEach(item => {
321
+ copyRecursive(path.join(src, item), path.join(dest, item));
322
+ });
323
+ } else if (stats.isFile()) {
324
+ // Copy file
325
+ fs.copyFileSync(src, dest);
326
+ console.log(` -> Copied ${src} to ${dest}`);
327
+ }
328
+ }
329
+
330
+ console.log('Copying static files...');
331
+ const items = fs.readdirSync(staticDir);
332
+ items.forEach(item => {
333
+ const srcPath = path.join(staticDir, item);
334
+ const destPath = path.join(outputDir, item);
335
+ copyRecursive(srcPath, destPath);
336
+ });
337
+ }
338
+
339
+ // Start build process
340
+ console.log('Starting build process...');
341
+
342
+ // Copy static files first (they can be overwritten by pages)
343
+ copyStaticFiles();
344
+
345
+ // Process all pages (can overwrite static files)
346
+ processAllPages('');
347
+
348
+ console.log('Build complete!');
349
+ };
350
+ }
package/src/index.js CHANGED
@@ -0,0 +1,3 @@
1
+ export { createSiteBuilder } from './createSiteBuilder.js';
2
+ export { createRtglMarkdown } from './rtglMarkdown.js';
3
+ export { default as rtglMarkdown } from './rtglMarkdown.js';
@@ -0,0 +1,126 @@
1
+ import MarkdownIt from 'markdown-it';
2
+
3
+ // Simple slug generation function
4
+ function generateSlug(text) {
5
+ return text
6
+ .toLowerCase()
7
+ .trim()
8
+ .replace(/[^\w\s-]/g, '')
9
+ .replace(/[\s_-]+/g, '-')
10
+ .replace(/^-+|-+$/g, '');
11
+ }
12
+
13
+ /**
14
+ * Custom Markdown renderer configuration for Rettangoli
15
+ * Adds rtgl-specific elements and styling
16
+ */
17
+ export const createRtglMarkdown = () => {
18
+ const md = MarkdownIt({
19
+ // Additional configuration can be added here
20
+ });
21
+
22
+ // Header configuration
23
+ md.renderer.rules.heading_open = (tokens, idx, options, env, self) => {
24
+ const token = tokens[idx];
25
+ const level = token.markup.length;
26
+ const inlineToken = tokens[idx + 1];
27
+ const headingText = inlineToken.content;
28
+ const id = generateSlug(headingText);
29
+
30
+ // Map heading levels to size values
31
+ const sizes = { 1: "h1", 2: "h2", 3: "h3", 4: "h4" };
32
+ const size = sizes[level] || "md";
33
+
34
+ return `<rtgl-text id="${id}" mt="lg" s="${size}" mb="md"> <a href="#${id}" style="display: contents;">`;
35
+ };
36
+
37
+ md.renderer.rules.heading_close = () => "</a></rtgl-text>\n";
38
+
39
+ // Paragraph configuration
40
+ md.renderer.rules.paragraph_open = (tokens, idx, options, env, self) => {
41
+ // Check if we're inside a list item
42
+ let isInListItem = false;
43
+ for (let i = idx - 1; i >= 0; i--) {
44
+ if (tokens[i].type === 'list_item_open') {
45
+ isInListItem = true;
46
+ break;
47
+ }
48
+ if (tokens[i].type === 'list_item_close') {
49
+ break;
50
+ }
51
+ }
52
+
53
+ // Don't wrap paragraphs in list items with rtgl-text
54
+ if (isInListItem) {
55
+ return '';
56
+ }
57
+ return `<rtgl-text s="bl" mb="lg">`;
58
+ };
59
+
60
+ md.renderer.rules.paragraph_close = (tokens, idx, options, env, self) => {
61
+ // Check if we're inside a list item
62
+ let isInListItem = false;
63
+ for (let i = idx - 1; i >= 0; i--) {
64
+ if (tokens[i].type === 'list_item_open') {
65
+ isInListItem = true;
66
+ break;
67
+ }
68
+ if (tokens[i].type === 'list_item_close') {
69
+ break;
70
+ }
71
+ }
72
+
73
+ // Don't wrap paragraphs in list items with rtgl-text
74
+ if (isInListItem) {
75
+ return '\n';
76
+ }
77
+ return "</rtgl-text>\n";
78
+ };
79
+
80
+ // Table configuration
81
+ md.renderer.rules.table_open = () => '<rtgl-view w="f">\n<table>';
82
+ md.renderer.rules.table_close = () => "</table>\n</rtgl-view>";
83
+
84
+ // Link configuration - add target="_blank" to all external links
85
+ md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
86
+ const token = tokens[idx];
87
+ const targetIndex = token.attrIndex("target");
88
+ const href =
89
+ (token.attrs && token.attrs.find((attr) => attr[0] === "href")?.[1]) ||
90
+ "";
91
+ const isExternal = href.startsWith("http") || href.startsWith("//");
92
+
93
+ // If this is an external link or already has target="_blank"
94
+ if (isExternal || targetIndex >= 0) {
95
+ if (targetIndex < 0) {
96
+ token.attrPush(["target", "_blank"]);
97
+ }
98
+ token.attrPush(["rel", "noreferrer"]);
99
+
100
+ // Find the next text token to use for the aria-label
101
+ let nextIdx = idx + 1;
102
+ let textContent = "";
103
+ while (nextIdx < tokens.length && tokens[nextIdx].type !== "link_close") {
104
+ if (tokens[nextIdx].type === "text") {
105
+ textContent += tokens[nextIdx].content;
106
+ }
107
+ nextIdx++;
108
+ }
109
+
110
+ // Add aria-label for external links
111
+ if (textContent.trim() && token.attrIndex("aria-label") < 0) {
112
+ token.attrPush([
113
+ "aria-label",
114
+ `${textContent.trim()} (opens in new tab)`,
115
+ ]);
116
+ }
117
+ }
118
+
119
+ return self.renderToken(tokens, idx, options);
120
+ };
121
+
122
+ return md;
123
+ };
124
+
125
+ // Export a default instance for convenience
126
+ export default createRtglMarkdown();
@@ -1,19 +0,0 @@
1
- <rtgl-text><a href="{{ back.href }}" aria-label="{{ back.text }}">{{ back.text }}</a></rtgl-text>
2
- <rtgl-text s="h2" mt="l">{{ title }}</rtgl-text>
3
- <rtgl-view mt="l">
4
- {{ subtitle }}
5
- </rtgl-view>
6
- <rtgl-view g="l" mt="xl">
7
- {%- for item in items -%}
8
-
9
- <rtgl-view w="f" d="h" av="c">
10
- {% if item.date %}
11
- <rtgl-view d="h" w="120">
12
- <rtgl-text>{{ item.date | postDate }}</rtgl-text>
13
- </rtgl-view>
14
- {% endif %}
15
- <rtgl-text s="lg"><a href="{{ item.url }}">{{ item.title }}</a></rtgl-text>
16
- </rtgl-view>
17
-
18
- {%- endfor -%}
19
- </rtgl-view>
@@ -1,9 +0,0 @@
1
- <rtgl-view w="f" ph="xl">
2
- <rtgl-view w="f" ah="c" bw="s" br="l" p="xl">
3
- <rtgl-text s="h3" ta="c" mt="l"> {{ cta.title }} </rtgl-text>
4
- <rtgl-text mt="l" ta="c" c="mu-fg"> {{ cta.description }} </rtgl-text>
5
- <rtgl-button mt="xl" href="{{ cta.cta.href }}" target="_blank" rel="noreferrer"> {{
6
- cta.cta.text
7
- }} </rtgl-button>
8
- </rtgl-view>
9
- </rtgl-view>
@@ -1,24 +0,0 @@
1
- <rtgl-view d="h" h="64"></rtgl-view>
2
- <rtgl-view pv="xl" mv="xl" mh="l" g="xl" ah="c">
3
- <rtgl-text s="h2" at="c"> {{ feature.title }} </rtgl-text>
4
- <rtgl-view d="h" w="f" g="xl" av="c" mt="l">
5
- <rtgl-view flex="2" mb="m" g="m" style="min-width: 320px;">
6
- <rtgl-text s="bl" mt="l"> {{ feature.description }} </rtgl-text>
7
- <ul>
8
- <rtgl-view >
9
- {%- for item in feature.items -%}
10
- <li>
11
- <rtgl-view>
12
- <rtgl-text mt="l"><b> {{ item.label }}:</b> {{ item.description }} </rtgl-text>
13
- </rtgl-view>
14
- </li>
15
- {%- endfor -%}
16
- </rtgl-view>
17
- </ul>
18
- </rtgl-view>
19
- <rtgl-view flex="3" style="min-width: 320px; overflow: hidden;" bw="xs" br="s">
20
- <rtgl-image src="{{ feature.picture }}" w="f" alt="{{ feature.imageAlt | default: feature.title }}"></rtgl-image>
21
- </rtgl-view>
22
- </rtgl-view>
23
- </rtgl-view>
24
- <rtgl-view d="h" h="64"></rtgl-view>
@@ -1,4 +0,0 @@
1
- <rtgl-view ah="c" w="f">
2
- <rtgl-text s="h1"> {{ hero.title }} </rtgl-text>
3
- <rtgl-text s="lg" mt="l" at="c"> {{ hero.message }} <rtgl-text>
4
- </rtgl-view>
@@ -1,31 +0,0 @@
1
- {%- if hero.video -%}
2
- <rtgl-view h="8vh"></rtgl-view>
3
- {%- else -%}
4
- <rtgl-view h="20vh"></rtgl-view>
5
- {%- endif -%}
6
- <rtgl-view w="f" pv="xl" av="c" ah="c" ph="xl">
7
- <rtgl-text s="h1" ta="c" mb="lg"> {{ hero.title }} </rtgl-text>
8
- {%- for subtitle in hero.subtitles -%}
9
- <rtgl-text s="lg" ta="c"> {{ subtitle }} </rtgl-text>
10
- {%- endfor -%}
11
- {%- if hero.cta -%}
12
- <rtgl-button mt="xl" href="{{ hero.cta.href }}" {% if hero.cta.openNewTab %}target="_blank" rel="noreferrer"{% endif %}> {{
13
- hero.cta.text
14
- }} </rtgl-button>
15
- {%- endif -%}
16
- </rtgl-view>
17
-
18
- {%- if hero.video-%}
19
- <rtgl-view w="f" ah="c" mt="xl">
20
-
21
- <rtgl-view lg-h="calc((100vw - 32px) / 1.777)" lg-w="calc(100vw - 32px)" h="calc(66.66vw / 1.777)" w="66.66vw"
22
- bw="xs" br="xs" shadow="sm">
23
- <video playsinline loop controls width="100%" aria-label="{{ hero.video.title | default: 'Video content' }}">
24
- <source src="{{ hero.video.url }}" type="video/mp4">
25
- </video>
26
- </rtgl-view>
27
- </rtgl-view>
28
- {%- else -%}
29
- <rtgl-view h="20vh">
30
- </rtgl-view>
31
- {%- endif -%}
@@ -1,15 +0,0 @@
1
- <rtgl-view g="m" w="f" mt="xl">
2
- <rtgl-text s="h3">{{ sectionTitle }}</rtgl-text>
3
- <rtgl-view g="l" w="f">
4
- {% for item in items %}
5
- <a href="{{ item.href }}" style="display: contents; color: inherit;">
6
- <rtgl-view bgc="mu" bw="s" br="m" p="m" w="f" g="xs">
7
- <rtgl-view d="h" g="m">
8
- <rtgl-text s="h4">{{ item.label }}</rtgl-text>
9
- </rtgl-view>
10
- <rtgl-text>{{ item.description }}</rtgl-text>
11
- </rtgl-view>
12
- </a>
13
- {% endfor %}
14
- </rtgl-view>
15
- </rtgl-view>
@@ -1 +0,0 @@
1
- <rtgl-view h="{{ height }}"></rtgl-view>
@@ -1,2 +0,0 @@
1
- <rtgl-table columns="{{ columns | json-escaped }}" rows="{{ rows | json-escaped }}" id="xxx">
2
- </rtgl-table>
@@ -1,21 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- {% include "core/htmlHeader.html" %}
6
- {% include "core/htmlHeaderTable.html" %}
7
- </head>
8
-
9
- <body class="dark">
10
- <rtgl-view w="f" ah="c" h="f">
11
- <rtgl-view d="h" w="f" h="f">
12
- <rtgl-sidebar title="{{ docs.title | json-escaped }}" items="{{ admin.items | json-escaped }}"></rtgl-sidebar>
13
- <rtgl-view h="100vh" w="f" sv id="content-container" ph="l">
14
- {{ content }}
15
- <rtgl-view h="50vh"></rtgl-view>
16
- </rtgl-view>
17
- </rtgl-view>
18
- </rtgl-view>
19
- </body>
20
-
21
- </html>