@refrakt-md/lumina 0.4.0 → 0.5.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.
Files changed (71) hide show
  1. package/base.css +16 -0
  2. package/contracts/structures.json +1317 -3
  3. package/dist/config.d.ts +2 -3
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +4 -229
  6. package/dist/config.js.map +1 -1
  7. package/dist/transform.d.ts +2 -0
  8. package/dist/transform.d.ts.map +1 -1
  9. package/dist/transform.js +2 -0
  10. package/dist/transform.js.map +1 -1
  11. package/index.css +11 -0
  12. package/package.json +18 -11
  13. package/styles/elements/blockquote.css +8 -4
  14. package/styles/global.css +0 -7
  15. package/styles/layouts/blog.css +255 -0
  16. package/styles/layouts/default.css +11 -3
  17. package/styles/layouts/docs.css +67 -13
  18. package/styles/layouts/mobile.css +84 -0
  19. package/styles/runes/bento.css +2 -0
  20. package/styles/runes/codegroup.css +7 -2
  21. package/styles/runes/design-context.css +25 -0
  22. package/styles/runes/feature.css +20 -14
  23. package/styles/runes/form.css +1 -2
  24. package/styles/runes/grid.css +25 -7
  25. package/styles/runes/hero.css +15 -0
  26. package/styles/runes/map.css +113 -0
  27. package/styles/runes/palette.css +86 -0
  28. package/styles/runes/preview.css +187 -0
  29. package/styles/runes/sandbox.css +23 -0
  30. package/styles/runes/spacing.css +105 -0
  31. package/styles/runes/steps.css +7 -1
  32. package/styles/runes/swatch.css +28 -0
  33. package/styles/runes/symbol.css +164 -0
  34. package/styles/runes/tabs.css +6 -0
  35. package/styles/runes/testimonial.css +2 -3
  36. package/styles/runes/timeline.css +43 -24
  37. package/styles/runes/typography.css +91 -0
  38. package/svelte/elements.ts +1 -0
  39. package/{sveltekit → svelte}/index.ts +0 -8
  40. package/svelte/layouts/BlogLayout.svelte +173 -0
  41. package/svelte/layouts/DefaultLayout.svelte +67 -0
  42. package/svelte/layouts/DocsLayout.svelte +155 -0
  43. package/{sveltekit → svelte}/manifest.json +1 -1
  44. package/svelte/registry.ts +2 -0
  45. package/svelte/tokens.css +6 -0
  46. package/sveltekit/components/Accordion.svelte +0 -26
  47. package/sveltekit/components/Bento.svelte +0 -50
  48. package/sveltekit/components/Chart.svelte +0 -121
  49. package/sveltekit/components/CodeGroup.svelte +0 -88
  50. package/sveltekit/components/Comparison.svelte +0 -209
  51. package/sveltekit/components/DataTable.svelte +0 -154
  52. package/sveltekit/components/Details.svelte +0 -23
  53. package/sveltekit/components/Diagram.svelte +0 -45
  54. package/sveltekit/components/Embed.svelte +0 -36
  55. package/sveltekit/components/Form.svelte +0 -194
  56. package/sveltekit/components/Grid.svelte +0 -42
  57. package/sveltekit/components/Nav.svelte +0 -62
  58. package/sveltekit/components/Pricing.svelte +0 -20
  59. package/sveltekit/components/Reveal.svelte +0 -62
  60. package/sveltekit/components/Storyboard.svelte +0 -41
  61. package/sveltekit/components/Tabs.svelte +0 -75
  62. package/sveltekit/components/Testimonial.svelte +0 -26
  63. package/sveltekit/elements/Blockquote.svelte +0 -37
  64. package/sveltekit/elements/Pre.svelte +0 -77
  65. package/sveltekit/elements/Table.svelte +0 -40
  66. package/sveltekit/elements.ts +0 -11
  67. package/sveltekit/layouts/BlogLayout.svelte +0 -382
  68. package/sveltekit/layouts/DefaultLayout.svelte +0 -70
  69. package/sveltekit/layouts/DocsLayout.svelte +0 -133
  70. package/sveltekit/registry.ts +0 -59
  71. package/sveltekit/tokens.css +0 -71
@@ -1,154 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from 'svelte';
3
- import type { Snippet } from 'svelte';
4
- import type { SerializedTag } from '@refrakt-md/svelte';
5
-
6
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
7
-
8
- const sortable = (tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'sortable')?.attributes?.content || '').split(',').map((s: string) => s.trim()).filter(Boolean);
9
- const searchable = tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'searchable')?.attributes?.content === 'true';
10
- const pageSize = parseInt(tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'pageSize')?.attributes?.content || '0', 10);
11
- const defaultSort = tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'defaultSort')?.attributes?.content || '';
12
-
13
- let searchQuery = $state('');
14
- let sortColumn = $state(defaultSort);
15
- let sortDirection = $state<'asc' | 'desc'>('asc');
16
- let currentPage = $state(0);
17
-
18
- let contentEl: HTMLDivElement;
19
- let headers: string[] = [];
20
- let allRows: { el: HTMLTableRowElement; cells: string[] }[] = [];
21
- let totalFiltered = $state(0);
22
-
23
- function toggleSort(column: string) {
24
- if (!sortable.includes(column)) return;
25
- if (sortColumn === column) {
26
- sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
27
- } else {
28
- sortColumn = column;
29
- sortDirection = 'asc';
30
- }
31
- currentPage = 0;
32
- }
33
-
34
- onMount(() => {
35
- const table = contentEl?.querySelector('table');
36
- if (!table) return;
37
-
38
- const thEls = table.querySelectorAll('th');
39
- headers = Array.from(thEls).map(th => th.textContent?.trim() || '');
40
-
41
- thEls.forEach((th) => {
42
- const name = th.textContent?.trim() || '';
43
- if (sortable.includes(name)) {
44
- th.style.cursor = 'pointer';
45
- th.style.userSelect = 'none';
46
- th.addEventListener('click', () => toggleSort(name));
47
- }
48
- });
49
-
50
- const bodyRows = table.querySelectorAll('tbody tr');
51
- const rows = bodyRows.length > 0 ? bodyRows : table.querySelectorAll('tr:not(:first-child)');
52
- allRows = Array.from(rows).map(tr => ({
53
- el: tr as HTMLTableRowElement,
54
- cells: Array.from(tr.querySelectorAll('td')).map(td => td.textContent?.trim() || ''),
55
- }));
56
-
57
- totalFiltered = allRows.length;
58
- });
59
-
60
- $effect(() => {
61
- if (!allRows.length) return;
62
-
63
- let filtered = [...allRows];
64
-
65
- if (searchQuery) {
66
- const q = searchQuery.toLowerCase();
67
- filtered = filtered.filter(r => r.cells.some(c => c.toLowerCase().includes(q)));
68
- }
69
-
70
- if (sortColumn) {
71
- const idx = headers.indexOf(sortColumn);
72
- if (idx >= 0) {
73
- filtered.sort((a, b) => {
74
- const cmp = a.cells[idx].localeCompare(b.cells[idx], undefined, { numeric: true });
75
- return sortDirection === 'asc' ? cmp : -cmp;
76
- });
77
- }
78
- }
79
-
80
- totalFiltered = filtered.length;
81
-
82
- const visible = pageSize > 0
83
- ? filtered.slice(currentPage * pageSize, (currentPage + 1) * pageSize)
84
- : filtered;
85
-
86
- const tbody = contentEl?.querySelector('tbody') || contentEl?.querySelector('table');
87
- if (!tbody) return;
88
-
89
- allRows.forEach(r => r.el.style.display = 'none');
90
- visible.forEach(r => {
91
- r.el.style.display = '';
92
- tbody.appendChild(r.el);
93
- });
94
-
95
- const thEls = contentEl?.querySelectorAll('th');
96
- thEls?.forEach(th => {
97
- const name = th.textContent?.replace(/[▲▼]/g, '').trim() || '';
98
- const indicator = th.querySelector('.sort-indicator');
99
- if (sortable.includes(name)) {
100
- if (sortColumn === name) {
101
- if (indicator) {
102
- indicator.textContent = sortDirection === 'asc' ? ' ▲' : ' ▼';
103
- } else {
104
- const span = document.createElement('span');
105
- span.className = 'sort-indicator';
106
- span.textContent = sortDirection === 'asc' ? ' ▲' : ' ▼';
107
- th.appendChild(span);
108
- }
109
- } else {
110
- if (indicator) indicator.remove();
111
- }
112
- }
113
- });
114
- });
115
-
116
- const totalPages = $derived(pageSize > 0 ? Math.ceil(totalFiltered / pageSize) : 1);
117
- </script>
118
-
119
- <div class="rf-datatable" typeof="DataTable">
120
- {#if searchable}
121
- <div class="rf-datatable__toolbar">
122
- <input
123
- type="search"
124
- placeholder="Filter rows..."
125
- bind:value={searchQuery}
126
- class="rf-datatable__input"
127
- />
128
- </div>
129
- {/if}
130
- <div class="rf-datatable__content" bind:this={contentEl}>
131
- {@render children()}
132
- </div>
133
- {#if pageSize > 0 && totalPages > 1}
134
- <div class="rf-datatable__pagination">
135
- <button
136
- class="rf-datatable__page-btn"
137
- disabled={currentPage === 0}
138
- onclick={() => currentPage--}
139
- >
140
- &larr; Prev
141
- </button>
142
- <span class="rf-datatable__page-info">
143
- {currentPage + 1} / {totalPages}
144
- </span>
145
- <button
146
- class="rf-datatable__page-btn"
147
- disabled={currentPage >= totalPages - 1}
148
- onclick={() => currentPage++}
149
- >
150
- Next &rarr;
151
- </button>
152
- </div>
153
- {/if}
154
- </div>
@@ -1,23 +0,0 @@
1
- <script lang="ts">
2
- import type { SerializedTag } from '@refrakt-md/svelte';
3
- import type { Snippet } from 'svelte';
4
-
5
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
6
-
7
- const summary = $derived(tag.children
8
- .find((c: any) => c?.name === 'span' && c?.attributes?.property === 'summary')
9
- ?.children?.[0] ?? 'Details');
10
-
11
- const isOpen = $derived(tag.children
12
- .find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'open')
13
- ?.attributes?.content ?? false);
14
- </script>
15
-
16
- <details class="rf-details" open={isOpen || undefined}>
17
- <summary class="rf-details__summary">
18
- <span>{summary}</span>
19
- </summary>
20
- <div class="rf-details__body">
21
- {@render children()}
22
- </div>
23
- </details>
@@ -1,45 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from 'svelte';
3
- import type { Snippet } from 'svelte';
4
- import type { SerializedTag } from '@refrakt-md/svelte';
5
-
6
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
7
-
8
- const language = tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'language')?.attributes?.content || 'mermaid';
9
- const title = tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'title')?.attributes?.content || '';
10
- const source = tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.['data-name'] === 'source')?.attributes?.content || '';
11
-
12
- let container: HTMLDivElement;
13
- let rendered = $state(false);
14
-
15
- onMount(async () => {
16
- if (language === 'mermaid' && source) {
17
- try {
18
- const cdn = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
19
- const mermaid = (await import(/* @vite-ignore */ cdn)).default;
20
- mermaid.initialize({ startOnLoad: false, theme: 'default' });
21
- const { svg } = await mermaid.render('mermaid-' + Math.random().toString(36).slice(2), source);
22
- container.innerHTML = svg;
23
- rendered = true;
24
- } catch (e) {
25
- container.textContent = source;
26
- }
27
- } else if (language === 'ascii' && source) {
28
- container.textContent = source;
29
- container.style.fontFamily = 'var(--rf-font-mono)';
30
- container.style.whiteSpace = 'pre';
31
- rendered = true;
32
- }
33
- });
34
- </script>
35
-
36
- <figure class="rf-diagram" typeof="Diagram">
37
- {#if title}
38
- <figcaption class="rf-diagram__title">{title}</figcaption>
39
- {/if}
40
- <div class="rf-diagram__container" bind:this={container}>
41
- {#if !rendered}
42
- <pre class="rf-diagram__source"><code>{source}</code></pre>
43
- {/if}
44
- </div>
45
- </figure>
@@ -1,36 +0,0 @@
1
- <script lang="ts">
2
- import type { SerializedTag } from '@refrakt-md/svelte';
3
- import type { Snippet } from 'svelte';
4
-
5
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
6
-
7
- const getMeta = (prop: string) => tag.children
8
- .find((c: any) => c?.name === 'meta' && c?.attributes?.property === prop)
9
- ?.attributes?.content;
10
-
11
- const embedUrl = getMeta('embedUrl') || getMeta('url') || '';
12
- const title = getMeta('title') || 'Embedded content';
13
- const aspect = getMeta('aspect') || '16:9';
14
- const provider = getMeta('provider') || '';
15
-
16
- const [w, h] = aspect.split(':').map(Number);
17
- const paddingPercent = h && w ? (h / w) * 100 : 56.25;
18
- </script>
19
-
20
- <figure class="rf-embed" data-provider={provider || undefined}>
21
- {#if embedUrl}
22
- <div class="rf-embed__wrapper" style="padding-bottom: {paddingPercent}%">
23
- <iframe
24
- src={embedUrl}
25
- {title}
26
- frameborder="0"
27
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
28
- allowfullscreen
29
- loading="lazy"
30
- ></iframe>
31
- </div>
32
- {/if}
33
- <div class="rf-embed__fallback">
34
- {@render children()}
35
- </div>
36
- </figure>
@@ -1,194 +0,0 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import type { SerializedTag } from '@refrakt-md/svelte';
4
-
5
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
6
-
7
- const isForm = tag.attributes.typeof === 'Form';
8
-
9
- // Form-level properties
10
- const action = isForm
11
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'action')?.attributes?.content || ''
12
- : '';
13
- const method = isForm
14
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'method')?.attributes?.content || 'POST'
15
- : 'POST';
16
- const success = isForm
17
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'success')?.attributes?.content || ''
18
- : '';
19
- const errorMsg = isForm
20
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'error')?.attributes?.content || ''
21
- : '';
22
- const formStyle = isForm
23
- ? tag.attributes['data-style'] || 'stacked'
24
- : 'stacked';
25
- const honeypot = isForm
26
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'honeypot')?.attributes?.content !== 'false'
27
- : true;
28
-
29
- // FormField properties
30
- const fieldName = !isForm
31
- ? tag.children.find((c: any) => c?.name === 'span' && c?.attributes?.property === 'name')?.children?.[0] || ''
32
- : '';
33
- const fieldType = !isForm
34
- ? tag.attributes['data-field-type'] || 'text'
35
- : '';
36
- const required = !isForm
37
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'required')?.attributes?.content === 'true'
38
- : false;
39
- const placeholder = !isForm
40
- ? tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'placeholder')?.attributes?.content || ''
41
- : '';
42
- const options = !isForm
43
- ? (tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'options')?.attributes?.content || '')
44
- .split(',')
45
- .map((o: string) => o.trim())
46
- .filter(Boolean)
47
- : [];
48
-
49
- const fieldId = !isForm ? `field-${fieldName.toLowerCase().replace(/\s+/g, '-')}` : '';
50
-
51
- // Form submission state
52
- let status = $state<'idle' | 'submitting' | 'success' | 'error'>('idle');
53
- let statusMessage = $state('');
54
-
55
- async function handleSubmit(e: SubmitEvent) {
56
- e.preventDefault();
57
- if (!action) return;
58
-
59
- status = 'submitting';
60
- const formData = new FormData(e.target as HTMLFormElement);
61
-
62
- try {
63
- const response = await fetch(action, {
64
- method: method,
65
- body: formData,
66
- headers: { 'Accept': 'application/json' },
67
- });
68
-
69
- if (response.ok) {
70
- status = 'success';
71
- statusMessage = success || 'Form submitted successfully.';
72
- (e.target as HTMLFormElement).reset();
73
- } else {
74
- status = 'error';
75
- statusMessage = errorMsg || 'Something went wrong. Please try again.';
76
- }
77
- } catch {
78
- status = 'error';
79
- statusMessage = errorMsg || 'Something went wrong. Please try again.';
80
- }
81
- }
82
- </script>
83
-
84
- {#if isForm}
85
- <form
86
- class="rf-form {formStyle !== 'stacked' ? `rf-form--${formStyle}` : ''}"
87
- action={action}
88
- method={method}
89
- onsubmit={handleSubmit}
90
- >
91
- {#if honeypot}
92
- <div class="rf-form__hp" aria-hidden="true">
93
- <input type="text" name="_gotcha" autocomplete="off" tabindex={-1} />
94
- </div>
95
- {/if}
96
- {@render children()}
97
- {#if status === 'submitting'}
98
- <div class="rf-form__status rf-form__status--submitting" role="status">Submitting...</div>
99
- {:else if status === 'success'}
100
- <div class="rf-form__status rf-form__status--success" role="alert">{statusMessage}</div>
101
- {:else if status === 'error'}
102
- <div class="rf-form__status rf-form__status--error" role="alert">{statusMessage}</div>
103
- {/if}
104
- </form>
105
- {:else if fieldType === 'group'}
106
- <fieldset class="rf-form-fieldset">
107
- <legend>{fieldName}</legend>
108
- {@render children()}
109
- </fieldset>
110
- {:else if fieldType === 'submit'}
111
- <button type="submit" class="rf-form__submit">{fieldName}</button>
112
- {:else if fieldType === 'separator'}
113
- <hr class="rf-form__separator" />
114
- {:else if fieldType === 'help'}
115
- <p class="rf-form__help">{fieldName}</p>
116
- {:else if fieldType === 'description'}
117
- <p class="rf-form__text">{fieldName}</p>
118
- {:else if fieldType === 'textarea'}
119
- <div class="rf-form-field">
120
- <label for={fieldId}>
121
- {fieldName}
122
- {#if required}<span class="rf-form-field__required" aria-hidden="true">*</span>{/if}
123
- </label>
124
- <textarea
125
- id={fieldId}
126
- name={fieldId}
127
- placeholder={placeholder}
128
- required={required}
129
- rows={4}
130
- ></textarea>
131
- </div>
132
- {:else if fieldType === 'select'}
133
- <div class="rf-form-field">
134
- <label for={fieldId}>
135
- {fieldName}
136
- {#if required}<span class="rf-form-field__required" aria-hidden="true">*</span>{/if}
137
- </label>
138
- <select id={fieldId} name={fieldId} required={required}>
139
- <option value="" disabled selected>Select an option</option>
140
- {#each options as option}
141
- <option value={option}>{option}</option>
142
- {/each}
143
- </select>
144
- </div>
145
- {:else if fieldType === 'radio'}
146
- <fieldset class="rf-form-field rf-form-choice-group">
147
- <legend>
148
- {fieldName}
149
- {#if required}<span class="rf-form-field__required" aria-hidden="true">*</span>{/if}
150
- </legend>
151
- {#each options as option, i}
152
- <label class="rf-form-choice">
153
- <input
154
- type="radio"
155
- name={fieldId}
156
- value={option}
157
- required={required && i === 0}
158
- />
159
- <span>{option}</span>
160
- </label>
161
- {/each}
162
- </fieldset>
163
- {:else if fieldType === 'checkbox'}
164
- <fieldset class="rf-form-field rf-form-choice-group">
165
- <legend>
166
- {fieldName}
167
- {#if required}<span class="rf-form-field__required" aria-hidden="true">*</span>{/if}
168
- </legend>
169
- {#each options as option}
170
- <label class="rf-form-choice">
171
- <input
172
- type="checkbox"
173
- name={fieldId}
174
- value={option}
175
- />
176
- <span>{option}</span>
177
- </label>
178
- {/each}
179
- </fieldset>
180
- {:else}
181
- <div class="rf-form-field">
182
- <label for={fieldId}>
183
- {fieldName}
184
- {#if required}<span class="rf-form-field__required" aria-hidden="true">*</span>{/if}
185
- </label>
186
- <input
187
- type={fieldType}
188
- id={fieldId}
189
- name={fieldId}
190
- placeholder={placeholder}
191
- required={required}
192
- />
193
- </div>
194
- {/if}
@@ -1,42 +0,0 @@
1
- <script lang="ts">
2
- import type { SerializedTag, RendererNode } from '@refrakt-md/svelte';
3
- import { Renderer } from '@refrakt-md/svelte';
4
- import type { Snippet } from 'svelte';
5
-
6
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
7
-
8
- function isTag(n: RendererNode): n is SerializedTag {
9
- return n !== null && typeof n === 'object' && !Array.isArray(n) && (n as any).$$mdtype === 'Tag';
10
- }
11
-
12
- const layoutDiv = $derived(tag.children.find(
13
- (c): c is SerializedTag => isTag(c) && c.attributes?.['data-layout'] === 'grid'
14
- ));
15
- const columns = $derived(layoutDiv?.attributes?.['data-columns']);
16
- const flow = $derived(layoutDiv?.attributes?.['data-flow']);
17
- const gridItems = $derived(layoutDiv?.children?.filter((c): c is SerializedTag => isTag(c)) ?? []);
18
- </script>
19
-
20
- {#if gridItems.length > 0}
21
- <div
22
- class="rf-grid"
23
- style:grid-template-columns={columns ? `repeat(${columns}, 1fr)` : undefined}
24
- style:grid-auto-flow={flow || undefined}
25
- >
26
- {#each gridItems as item}
27
- {@const colspan = Number(item.attributes?.['data-colspan']) || 1}
28
- {@const rowspan = Number(item.attributes?.['data-rowspan']) || 1}
29
- <div
30
- class="rf-grid__item"
31
- style:grid-column={colspan > 1 ? `span ${colspan}` : undefined}
32
- style:grid-row={rowspan > 1 ? `span ${rowspan}` : undefined}
33
- >
34
- <Renderer node={item.children} />
35
- </div>
36
- {/each}
37
- </div>
38
- {:else}
39
- <div class="rf-grid">
40
- {@render children()}
41
- </div>
42
- {/if}
@@ -1,62 +0,0 @@
1
- <script lang="ts">
2
- import type { SerializedTag, RendererNode } from '@refrakt-md/svelte';
3
- import type { Snippet } from 'svelte';
4
- import { getContext } from 'svelte';
5
- import { page } from '$app/state';
6
-
7
- interface PageEntry {
8
- url: string;
9
- title: string;
10
- draft: boolean;
11
- }
12
-
13
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
14
- const typeName = $derived(tag.attributes.typeof);
15
- const pages = getContext<PageEntry[]>('pages');
16
-
17
- function getSlug(tag: SerializedTag): string | null {
18
- for (const child of tag.children) {
19
- if (isTag(child) && child.name === 'span' && child.attributes.property === 'slug') {
20
- return getTextContent(child);
21
- }
22
- }
23
- return null;
24
- }
25
-
26
- function isTag(node: RendererNode): node is SerializedTag {
27
- return node !== null && typeof node === 'object' && !Array.isArray(node) && (node as any).$$mdtype === 'Tag';
28
- }
29
-
30
- function getTextContent(node: RendererNode): string {
31
- if (typeof node === 'string') return node;
32
- if (typeof node === 'number') return String(node);
33
- if (isTag(node)) return node.children.map(getTextContent).join('');
34
- if (Array.isArray(node)) return node.map(getTextContent).join('');
35
- return '';
36
- }
37
-
38
- function resolvePage(slug: string): PageEntry | undefined {
39
- return pages?.find(p => p.url.endsWith('/' + slug) || p.url === '/' + slug);
40
- }
41
-
42
- const slug = $derived(typeName === 'NavItem' ? getSlug(tag) : null);
43
- const linkedPage = $derived(slug ? resolvePage(slug) : null);
44
- </script>
45
-
46
- {#if typeName === 'Nav'}
47
- <nav class="rf-nav">
48
- {@render children()}
49
- </nav>
50
- {:else if typeName === 'NavGroup'}
51
- <div class="rf-nav-group">
52
- {@render children()}
53
- </div>
54
- {:else if typeName === 'NavItem' && linkedPage}
55
- <a href={linkedPage.url} class="rf-nav-item__link" class:rf-nav-item__link--active={page.url.pathname === linkedPage.url}>
56
- {linkedPage.title}
57
- </a>
58
- {:else}
59
- <div class="rf-nav-item">
60
- {@render children()}
61
- </div>
62
- {/if}
@@ -1,20 +0,0 @@
1
- <script lang="ts">
2
- import type { SerializedTag } from '@refrakt-md/svelte';
3
- import type { Snippet } from 'svelte';
4
-
5
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
6
-
7
- const typeName = tag.attributes.typeof;
8
- const isTier = typeName === 'Tier' || typeName === 'FeaturedTier';
9
- const isFeatured = typeName === 'FeaturedTier';
10
- </script>
11
-
12
- {#if typeName === 'Pricing'}
13
- <section class="rf-pricing">
14
- {@render children()}
15
- </section>
16
- {:else if isTier}
17
- <li class="rf-tier {isFeatured ? 'rf-tier--featured' : ''}">
18
- {@render children()}
19
- </li>
20
- {/if}
@@ -1,62 +0,0 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import type { SerializedTag } from '@refrakt-md/svelte';
4
- import { Renderer } from '@refrakt-md/svelte';
5
-
6
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
7
-
8
- const isGroup = tag.attributes.typeof === 'Reveal';
9
-
10
- const stepName = !isGroup
11
- ? tag.children.find((c: any) => c?.name === 'span' && c?.attributes?.property === 'name')?.children?.[0] || ''
12
- : '';
13
-
14
- const mode = isGroup
15
- ? tag.children.find((c: any) => c?.name === 'meta')?.attributes?.content || 'click'
16
- : 'click';
17
-
18
- const stepsDiv = isGroup
19
- ? tag.children.find((c: any) => c?.name === 'div' && c?.attributes?.['data-name'] === 'steps')
20
- : null;
21
-
22
- const totalSteps = isGroup && stepsDiv
23
- ? (stepsDiv as any).children?.filter((c: any) => c?.attributes?.typeof === 'RevealStep')?.length ?? 0
24
- : 0;
25
-
26
- let visibleCount = $state(1);
27
- </script>
28
-
29
- {#if isGroup}
30
- <section class="rf-reveal">
31
- {#if stepsDiv}
32
- <div class="rf-reveal__steps">
33
- {#each (stepsDiv as any).children?.filter((c: any) => c?.attributes?.typeof === 'RevealStep') ?? [] as step, i}
34
- <div class="rf-reveal-step {i < visibleCount ? 'rf-reveal-step--visible' : 'rf-reveal-step--hidden'}">
35
- <Renderer node={step} />
36
- </div>
37
- {/each}
38
- </div>
39
- {:else}
40
- {@render children()}
41
- {/if}
42
- {#if mode === 'click' && visibleCount < totalSteps}
43
- <button class="rf-reveal__next" onclick={() => visibleCount++}>
44
- Continue
45
- </button>
46
- {/if}
47
- {#if mode === 'click' && visibleCount >= totalSteps && totalSteps > 1}
48
- <button class="rf-reveal__reset" onclick={() => visibleCount = 1}>
49
- Start over
50
- </button>
51
- {/if}
52
- </section>
53
- {:else}
54
- <div class="rf-reveal-step__content">
55
- {#if stepName}
56
- <h3 class="rf-reveal-step__title">{stepName}</h3>
57
- {/if}
58
- <div class="rf-reveal-step__body">
59
- {@render children()}
60
- </div>
61
- </div>
62
- {/if}
@@ -1,41 +0,0 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import type { SerializedTag, RendererNode } from '@refrakt-md/svelte';
4
- import { Renderer } from '@refrakt-md/svelte';
5
-
6
- let { tag, children }: { tag: SerializedTag; children: Snippet } = $props();
7
-
8
- function isTag(n: RendererNode): n is SerializedTag {
9
- return n !== null && typeof n === 'object' && !Array.isArray(n) && (n as any).$$mdtype === 'Tag';
10
- }
11
-
12
- const isGroup = $derived(tag.attributes.typeof === 'Storyboard');
13
-
14
- // Style modifier is consumed by engine; read from data attribute
15
- const styleName = $derived(isGroup
16
- ? tag.attributes['data-style'] || 'clean'
17
- : 'clean');
18
- const columns = $derived(isGroup
19
- ? parseInt(tag.children.find((c: any) => c?.name === 'meta' && c?.attributes?.property === 'columns')?.attributes?.content || '3', 10)
20
- : 3);
21
-
22
- const panelsEl = $derived(isGroup
23
- ? tag.children.find((c): c is SerializedTag => isTag(c) && c.attributes?.['data-name'] === 'panels')
24
- : undefined);
25
- </script>
26
-
27
- {#if isGroup}
28
- <div class="rf-storyboard rf-storyboard--{styleName}" style="--sb-columns: {columns};">
29
- <div class="rf-storyboard__panels">
30
- {#if panelsEl}
31
- <Renderer node={panelsEl.children} />
32
- {/if}
33
- </div>
34
- </div>
35
- {:else}
36
- <div class="rf-storyboard-panel">
37
- <div class="rf-storyboard-panel__body">
38
- {@render children()}
39
- </div>
40
- </div>
41
- {/if}