@refrakt-md/behaviors 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 (41) hide show
  1. package/dist/behaviors/accordion.d.ts +11 -0
  2. package/dist/behaviors/accordion.d.ts.map +1 -0
  3. package/dist/behaviors/accordion.js +85 -0
  4. package/dist/behaviors/accordion.js.map +1 -0
  5. package/dist/behaviors/copy.d.ts +9 -0
  6. package/dist/behaviors/copy.d.ts.map +1 -0
  7. package/dist/behaviors/copy.js +53 -0
  8. package/dist/behaviors/copy.js.map +1 -0
  9. package/dist/behaviors/datatable.d.ts +17 -0
  10. package/dist/behaviors/datatable.d.ts.map +1 -0
  11. package/dist/behaviors/datatable.js +210 -0
  12. package/dist/behaviors/datatable.js.map +1 -0
  13. package/dist/behaviors/form.d.ts +18 -0
  14. package/dist/behaviors/form.d.ts.map +1 -0
  15. package/dist/behaviors/form.js +88 -0
  16. package/dist/behaviors/form.js.map +1 -0
  17. package/dist/behaviors/preview.d.ts +20 -0
  18. package/dist/behaviors/preview.d.ts.map +1 -0
  19. package/dist/behaviors/preview.js +302 -0
  20. package/dist/behaviors/preview.js.map +1 -0
  21. package/dist/behaviors/reveal.d.ts +9 -0
  22. package/dist/behaviors/reveal.d.ts.map +1 -0
  23. package/dist/behaviors/reveal.js +70 -0
  24. package/dist/behaviors/reveal.js.map +1 -0
  25. package/dist/behaviors/tabs.d.ts +13 -0
  26. package/dist/behaviors/tabs.d.ts.map +1 -0
  27. package/dist/behaviors/tabs.js +142 -0
  28. package/dist/behaviors/tabs.js.map +1 -0
  29. package/dist/index.d.ts +20 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +61 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/types.d.ts +12 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/types.js +2 -0
  36. package/dist/types.js.map +1 -0
  37. package/dist/utils.d.ts +11 -0
  38. package/dist/utils.d.ts.map +1 -0
  39. package/dist/utils.js +18 -0
  40. package/dist/utils.js.map +1 -0
  41. package/package.json +31 -0
@@ -0,0 +1,11 @@
1
+ import type { CleanupFn } from '../types.js';
2
+ /**
3
+ * Accordion behavior for `[data-rune="accordion"]`.
4
+ *
5
+ * Enhances native `<details>/<summary>` elements with:
6
+ * - Exclusive mode: only one item open at a time (when `data-multiple` is absent)
7
+ * - ARIA attributes: `aria-expanded` on triggers, `aria-controls`/`aria-labelledby` wiring
8
+ * - Keyboard navigation: ArrowUp/Down between triggers, Home/End to first/last
9
+ */
10
+ export declare function accordionBehavior(el: HTMLElement): CleanupFn;
11
+ //# sourceMappingURL=accordion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../src/behaviors/accordion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,CAwF5D"}
@@ -0,0 +1,85 @@
1
+ import { uniqueId } from '../utils.js';
2
+ /**
3
+ * Accordion behavior for `[data-rune="accordion"]`.
4
+ *
5
+ * Enhances native `<details>/<summary>` elements with:
6
+ * - Exclusive mode: only one item open at a time (when `data-multiple` is absent)
7
+ * - ARIA attributes: `aria-expanded` on triggers, `aria-controls`/`aria-labelledby` wiring
8
+ * - Keyboard navigation: ArrowUp/Down between triggers, Home/End to first/last
9
+ */
10
+ export function accordionBehavior(el) {
11
+ const details = Array.from(el.querySelectorAll('details'));
12
+ if (details.length === 0)
13
+ return () => { };
14
+ const allowMultiple = el.hasAttribute('data-multiple');
15
+ const cleanups = [];
16
+ // Wire up ARIA and event listeners for each item
17
+ for (const item of details) {
18
+ const summary = item.querySelector('summary');
19
+ if (!summary)
20
+ continue;
21
+ // Generate IDs for ARIA wiring
22
+ const panelId = uniqueId('rf-accordion-panel');
23
+ const triggerId = uniqueId('rf-accordion-trigger');
24
+ summary.id = triggerId;
25
+ summary.setAttribute('aria-expanded', String(item.open));
26
+ // Find the content panel (first non-summary child element)
27
+ const panel = Array.from(item.children).find((child) => child instanceof HTMLElement && child.tagName !== 'SUMMARY');
28
+ if (panel) {
29
+ panel.id = panelId;
30
+ panel.setAttribute('role', 'region');
31
+ panel.setAttribute('aria-labelledby', triggerId);
32
+ summary.setAttribute('aria-controls', panelId);
33
+ }
34
+ // Toggle handler for exclusive mode
35
+ const onToggle = () => {
36
+ summary.setAttribute('aria-expanded', String(item.open));
37
+ if (!allowMultiple && item.open) {
38
+ for (const other of details) {
39
+ if (other !== item && other.open) {
40
+ other.open = false;
41
+ const otherSummary = other.querySelector('summary');
42
+ otherSummary?.setAttribute('aria-expanded', 'false');
43
+ }
44
+ }
45
+ }
46
+ };
47
+ item.addEventListener('toggle', onToggle);
48
+ cleanups.push(() => item.removeEventListener('toggle', onToggle));
49
+ }
50
+ // Keyboard navigation across all summaries
51
+ const summaries = details
52
+ .map((d) => d.querySelector('summary'))
53
+ .filter((s) => s !== null);
54
+ const onKeydown = (e) => {
55
+ const target = e.target;
56
+ const index = summaries.indexOf(target);
57
+ if (index === -1)
58
+ return;
59
+ let next = null;
60
+ switch (e.key) {
61
+ case 'ArrowDown':
62
+ next = (index + 1) % summaries.length;
63
+ break;
64
+ case 'ArrowUp':
65
+ next = (index - 1 + summaries.length) % summaries.length;
66
+ break;
67
+ case 'Home':
68
+ next = 0;
69
+ break;
70
+ case 'End':
71
+ next = summaries.length - 1;
72
+ break;
73
+ default:
74
+ return;
75
+ }
76
+ if (next !== null) {
77
+ e.preventDefault();
78
+ summaries[next].focus();
79
+ }
80
+ };
81
+ el.addEventListener('keydown', onKeydown);
82
+ cleanups.push(() => el.removeEventListener('keydown', onKeydown));
83
+ return () => cleanups.forEach((fn) => fn());
84
+ }
85
+ //# sourceMappingURL=accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion.js","sourceRoot":"","sources":["../../src/behaviors/accordion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { CleanupFn } from '../types.js';
2
+ /**
3
+ * Copy-to-clipboard behavior for code blocks.
4
+ *
5
+ * Finds all `<pre>` elements within the container and injects a copy button.
6
+ * Works on standalone code blocks and code blocks inside rune containers.
7
+ */
8
+ export declare function copyBehavior(container: HTMLElement | Document): CleanupFn;
9
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../src/behaviors/copy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAiDzE"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copy-to-clipboard behavior for code blocks.
3
+ *
4
+ * Finds all `<pre>` elements within the container and injects a copy button.
5
+ * Works on standalone code blocks and code blocks inside rune containers.
6
+ */
7
+ export function copyBehavior(container) {
8
+ const pres = container.querySelectorAll('pre');
9
+ const cleanups = [];
10
+ for (const pre of pres) {
11
+ // Skip if already wrapped with a copy button (by this behavior or by a framework component)
12
+ if (pre.parentElement?.classList.contains('rf-code-wrapper'))
13
+ continue;
14
+ if (pre.parentElement?.classList.contains('rf-codeblock'))
15
+ continue;
16
+ const btn = document.createElement('button');
17
+ btn.className = 'rf-copy-btn';
18
+ btn.type = 'button';
19
+ btn.setAttribute('aria-label', 'Copy code');
20
+ btn.textContent = 'Copy';
21
+ let timeout;
22
+ const handler = () => {
23
+ const text = pre.textContent ?? '';
24
+ navigator.clipboard.writeText(text).then(() => {
25
+ btn.textContent = 'Copied!';
26
+ btn.setAttribute('aria-label', 'Copied');
27
+ if (timeout)
28
+ clearTimeout(timeout);
29
+ timeout = setTimeout(() => {
30
+ btn.textContent = 'Copy';
31
+ btn.setAttribute('aria-label', 'Copy code');
32
+ }, 2000);
33
+ });
34
+ };
35
+ btn.addEventListener('click', handler);
36
+ // Wrap pre in a relative container for positioning if not already wrapped
37
+ const wrapper = document.createElement('div');
38
+ wrapper.className = 'rf-code-wrapper';
39
+ pre.parentNode?.insertBefore(wrapper, pre);
40
+ wrapper.appendChild(pre);
41
+ wrapper.appendChild(btn);
42
+ cleanups.push(() => {
43
+ btn.removeEventListener('click', handler);
44
+ if (timeout)
45
+ clearTimeout(timeout);
46
+ // Unwrap: move pre back out, remove wrapper and button
47
+ wrapper.parentNode?.insertBefore(pre, wrapper);
48
+ wrapper.remove();
49
+ });
50
+ }
51
+ return () => cleanups.forEach((fn) => fn());
52
+ }
53
+ //# sourceMappingURL=copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.js","sourceRoot":"","sources":["../../src/behaviors/copy.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiC;IAC7D,MAAM,IAAI,GAAG,SAAS,CAAC,gBAAgB,CAAc,KAAK,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,4FAA4F;QAC5F,IAAI,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAAE,SAAS;QACvE,IAAI,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC;YAAE,SAAS;QAEpE,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC7C,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC;QAC9B,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QACpB,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAC5C,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC;QAEzB,IAAI,OAAkD,CAAC;QAEvD,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;YACnC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC7C,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC;gBAC5B,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;gBACzC,IAAI,OAAO;oBAAE,YAAY,CAAC,OAAO,CAAC,CAAC;gBACnC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;oBACzB,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC;oBACzB,GAAG,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;gBAC7C,CAAC,EAAE,IAAI,CAAC,CAAC;YACV,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC;QAEF,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAEvC,0EAA0E;QAC1E,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC;QACtC,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC3C,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAEzB,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE;YAClB,GAAG,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1C,IAAI,OAAO;gBAAE,YAAY,CAAC,OAAO,CAAC,CAAC;YACnC,uDAAuD;YACvD,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { CleanupFn } from '../types.js';
2
+ /**
3
+ * DataTable behavior for `[data-rune="datatable"]`.
4
+ *
5
+ * Enhances a rendered `<table>` element with:
6
+ * - Search/filter: input that filters rows by cell text
7
+ * - Column sorting: clickable headers with sort indicators
8
+ * - Pagination: page navigation with prev/next buttons
9
+ *
10
+ * Reads configuration from data attributes set by the identity transform:
11
+ * - `data-searchable`: "true" to enable search
12
+ * - `data-sortable`: comma-separated column names
13
+ * - `data-pagesize`: number of rows per page (0 = no pagination)
14
+ * - `data-defaultsort`: column name to sort by initially
15
+ */
16
+ export declare function datatableBehavior(el: HTMLElement): CleanupFn;
17
+ //# sourceMappingURL=datatable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datatable.d.ts","sourceRoot":"","sources":["../../src/behaviors/datatable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,CAqO5D"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * DataTable behavior for `[data-rune="datatable"]`.
3
+ *
4
+ * Enhances a rendered `<table>` element with:
5
+ * - Search/filter: input that filters rows by cell text
6
+ * - Column sorting: clickable headers with sort indicators
7
+ * - Pagination: page navigation with prev/next buttons
8
+ *
9
+ * Reads configuration from data attributes set by the identity transform:
10
+ * - `data-searchable`: "true" to enable search
11
+ * - `data-sortable`: comma-separated column names
12
+ * - `data-pagesize`: number of rows per page (0 = no pagination)
13
+ * - `data-defaultsort`: column name to sort by initially
14
+ */
15
+ export function datatableBehavior(el) {
16
+ const table = el.querySelector('table');
17
+ if (!table)
18
+ return () => { };
19
+ // Read configuration from data attributes
20
+ const searchable = el.getAttribute('data-searchable') === 'true';
21
+ const sortableStr = el.getAttribute('data-sortable') || '';
22
+ const sortable = sortableStr.split(',').map((s) => s.trim()).filter(Boolean);
23
+ const pageSize = parseInt(el.getAttribute('data-pagesize') || '0', 10);
24
+ const defaultSort = el.getAttribute('data-defaultsort') || '';
25
+ // Parse table structure
26
+ const thEls = Array.from(table.querySelectorAll('th'));
27
+ const headers = thEls.map((th) => th.textContent?.trim() || '');
28
+ const bodyRows = table.querySelectorAll('tbody tr');
29
+ const rows = Array.from(bodyRows.length > 0 ? bodyRows : table.querySelectorAll('tr:not(:first-child)')).map((tr) => ({
30
+ el: tr,
31
+ cells: Array.from(tr.querySelectorAll('td')).map((td) => td.textContent?.trim() || ''),
32
+ }));
33
+ // Save original row order for cleanup
34
+ const originalOrder = rows.map((r) => r.el);
35
+ // State
36
+ let searchQuery = '';
37
+ let sortColumn = defaultSort;
38
+ let sortDirection = 'asc';
39
+ let currentPage = 0;
40
+ const cleanups = [];
41
+ // Inject search toolbar
42
+ let toolbar = null;
43
+ let searchInput = null;
44
+ if (searchable) {
45
+ toolbar = document.createElement('div');
46
+ toolbar.className = 'rf-datatable__toolbar';
47
+ searchInput = document.createElement('input');
48
+ searchInput.type = 'search';
49
+ searchInput.placeholder = 'Filter rows...';
50
+ searchInput.className = 'rf-datatable__input';
51
+ toolbar.appendChild(searchInput);
52
+ table.before(toolbar);
53
+ const onInput = () => {
54
+ searchQuery = searchInput.value;
55
+ currentPage = 0;
56
+ render();
57
+ };
58
+ searchInput.addEventListener('input', onInput);
59
+ cleanups.push(() => searchInput.removeEventListener('input', onInput));
60
+ }
61
+ // Make sortable headers clickable
62
+ for (const th of thEls) {
63
+ const name = th.textContent?.trim() || '';
64
+ if (!sortable.includes(name))
65
+ continue;
66
+ th.style.cursor = 'pointer';
67
+ th.style.userSelect = 'none';
68
+ const onClick = () => {
69
+ if (sortColumn === name) {
70
+ sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
71
+ }
72
+ else {
73
+ sortColumn = name;
74
+ sortDirection = 'asc';
75
+ }
76
+ currentPage = 0;
77
+ render();
78
+ };
79
+ th.addEventListener('click', onClick);
80
+ cleanups.push(() => {
81
+ th.removeEventListener('click', onClick);
82
+ th.style.cursor = '';
83
+ th.style.userSelect = '';
84
+ });
85
+ }
86
+ // Inject pagination controls
87
+ let pagination = null;
88
+ let prevBtn = null;
89
+ let nextBtn = null;
90
+ let pageInfo = null;
91
+ if (pageSize > 0) {
92
+ pagination = document.createElement('div');
93
+ pagination.className = 'rf-datatable__pagination';
94
+ prevBtn = document.createElement('button');
95
+ prevBtn.className = 'rf-datatable__page-btn';
96
+ prevBtn.innerHTML = '&larr; Prev';
97
+ pageInfo = document.createElement('span');
98
+ pageInfo.className = 'rf-datatable__page-info';
99
+ nextBtn = document.createElement('button');
100
+ nextBtn.className = 'rf-datatable__page-btn';
101
+ nextBtn.innerHTML = 'Next &rarr;';
102
+ pagination.appendChild(prevBtn);
103
+ pagination.appendChild(pageInfo);
104
+ pagination.appendChild(nextBtn);
105
+ table.after(pagination);
106
+ const onPrev = () => {
107
+ if (currentPage > 0) {
108
+ currentPage--;
109
+ render();
110
+ }
111
+ };
112
+ const onNext = () => {
113
+ currentPage++;
114
+ render();
115
+ };
116
+ prevBtn.addEventListener('click', onPrev);
117
+ nextBtn.addEventListener('click', onNext);
118
+ cleanups.push(() => {
119
+ prevBtn.removeEventListener('click', onPrev);
120
+ nextBtn.removeEventListener('click', onNext);
121
+ });
122
+ }
123
+ function render() {
124
+ let filtered = [...rows];
125
+ // Filter
126
+ if (searchQuery) {
127
+ const q = searchQuery.toLowerCase();
128
+ filtered = filtered.filter((r) => r.cells.some((c) => c.toLowerCase().includes(q)));
129
+ }
130
+ // Sort
131
+ if (sortColumn) {
132
+ const idx = headers.indexOf(sortColumn);
133
+ if (idx >= 0) {
134
+ filtered.sort((a, b) => {
135
+ const cmp = a.cells[idx].localeCompare(b.cells[idx], undefined, { numeric: true });
136
+ return sortDirection === 'asc' ? cmp : -cmp;
137
+ });
138
+ }
139
+ }
140
+ const totalFiltered = filtered.length;
141
+ const totalPages = pageSize > 0 ? Math.ceil(totalFiltered / pageSize) : 1;
142
+ // Clamp current page
143
+ if (currentPage >= totalPages)
144
+ currentPage = Math.max(0, totalPages - 1);
145
+ // Determine visible rows
146
+ const visible = pageSize > 0
147
+ ? filtered.slice(currentPage * pageSize, (currentPage + 1) * pageSize)
148
+ : filtered;
149
+ const tbody = table.querySelector('tbody') || table;
150
+ // Hide all, then show and reorder visible
151
+ for (const r of rows)
152
+ r.el.style.display = 'none';
153
+ for (const r of visible) {
154
+ r.el.style.display = '';
155
+ tbody.appendChild(r.el);
156
+ }
157
+ // Update sort indicators on headers
158
+ for (const th of thEls) {
159
+ const name = th.textContent?.replace(/[▲▼]/g, '').trim() || '';
160
+ const indicator = th.querySelector('.rf-datatable__sort-indicator');
161
+ if (sortable.includes(name)) {
162
+ if (sortColumn === name) {
163
+ if (indicator) {
164
+ indicator.textContent = sortDirection === 'asc' ? ' ▲' : ' ▼';
165
+ }
166
+ else {
167
+ const span = document.createElement('span');
168
+ span.className = 'rf-datatable__sort-indicator';
169
+ span.textContent = sortDirection === 'asc' ? ' ▲' : ' ▼';
170
+ th.appendChild(span);
171
+ }
172
+ }
173
+ else {
174
+ indicator?.remove();
175
+ }
176
+ }
177
+ }
178
+ // Update pagination
179
+ if (pagination && prevBtn && nextBtn && pageInfo) {
180
+ prevBtn.disabled = currentPage === 0;
181
+ nextBtn.disabled = currentPage >= totalPages - 1;
182
+ pageInfo.textContent = `${currentPage + 1} / ${totalPages}`;
183
+ if (totalPages <= 1) {
184
+ pagination.hidden = true;
185
+ }
186
+ else {
187
+ pagination.hidden = false;
188
+ }
189
+ }
190
+ }
191
+ // Initial render
192
+ render();
193
+ return () => {
194
+ cleanups.forEach((fn) => fn());
195
+ // Remove injected elements
196
+ toolbar?.remove();
197
+ pagination?.remove();
198
+ // Remove sort indicators
199
+ for (const th of thEls) {
200
+ th.querySelector('.rf-datatable__sort-indicator')?.remove();
201
+ }
202
+ // Restore original row order and visibility
203
+ const tbody = table.querySelector('tbody') || table;
204
+ for (const row of originalOrder) {
205
+ row.style.display = '';
206
+ tbody.appendChild(row);
207
+ }
208
+ };
209
+ }
210
+ //# sourceMappingURL=datatable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datatable.js","sourceRoot":"","sources":["../../src/behaviors/datatable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { CleanupFn } from '../types.js';
2
+ /**
3
+ * Form behavior for `[data-rune="form"]`.
4
+ *
5
+ * Enhances a rendered `<form>` element with:
6
+ * - Async submission via fetch (prevents full page reload)
7
+ * - Honeypot field for basic spam protection
8
+ * - Status messages (submitting, success, error) with ARIA roles
9
+ *
10
+ * Reads configuration from data attributes:
11
+ * - `data-action`: form submission URL
12
+ * - `data-method`: HTTP method (default: POST)
13
+ * - `data-success`: success message text
14
+ * - `data-error`: error message text
15
+ * - `data-honeypot`: "false" to disable honeypot (default: enabled)
16
+ */
17
+ export declare function formBehavior(el: HTMLElement): CleanupFn;
18
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/behaviors/form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,CAgFvD"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Form behavior for `[data-rune="form"]`.
3
+ *
4
+ * Enhances a rendered `<form>` element with:
5
+ * - Async submission via fetch (prevents full page reload)
6
+ * - Honeypot field for basic spam protection
7
+ * - Status messages (submitting, success, error) with ARIA roles
8
+ *
9
+ * Reads configuration from data attributes:
10
+ * - `data-action`: form submission URL
11
+ * - `data-method`: HTTP method (default: POST)
12
+ * - `data-success`: success message text
13
+ * - `data-error`: error message text
14
+ * - `data-honeypot`: "false" to disable honeypot (default: enabled)
15
+ */
16
+ export function formBehavior(el) {
17
+ // The element may be a <form> itself or contain one
18
+ const form = el.tagName === 'FORM' ? el : el.querySelector('form');
19
+ if (!form)
20
+ return () => { };
21
+ const action = el.getAttribute('data-action') || form.getAttribute('action') || '';
22
+ const method = el.getAttribute('data-method') || form.getAttribute('method') || 'POST';
23
+ const successMsg = el.getAttribute('data-success') || 'Form submitted successfully.';
24
+ const errorMsg = el.getAttribute('data-error') || 'Something went wrong. Please try again.';
25
+ const honeypotEnabled = el.getAttribute('data-honeypot') !== 'false';
26
+ // Inject honeypot field
27
+ let honeypotDiv = null;
28
+ if (honeypotEnabled) {
29
+ honeypotDiv = document.createElement('div');
30
+ honeypotDiv.className = 'rf-form__hp';
31
+ honeypotDiv.setAttribute('aria-hidden', 'true');
32
+ honeypotDiv.style.display = 'none';
33
+ const honeypotInput = document.createElement('input');
34
+ honeypotInput.type = 'text';
35
+ honeypotInput.name = '_gotcha';
36
+ honeypotInput.setAttribute('autocomplete', 'off');
37
+ honeypotInput.tabIndex = -1;
38
+ honeypotDiv.appendChild(honeypotInput);
39
+ form.prepend(honeypotDiv);
40
+ }
41
+ // Create status element
42
+ const statusEl = document.createElement('div');
43
+ statusEl.className = 'rf-form__status';
44
+ statusEl.hidden = true;
45
+ form.appendChild(statusEl);
46
+ function showStatus(type, message) {
47
+ statusEl.hidden = false;
48
+ statusEl.className = `rf-form__status rf-form__status--${type}`;
49
+ statusEl.setAttribute('role', type === 'submitting' ? 'status' : 'alert');
50
+ statusEl.textContent = message;
51
+ }
52
+ function hideStatus() {
53
+ statusEl.hidden = true;
54
+ statusEl.className = 'rf-form__status';
55
+ statusEl.textContent = '';
56
+ }
57
+ const onSubmit = async (e) => {
58
+ e.preventDefault();
59
+ if (!action)
60
+ return;
61
+ showStatus('submitting', 'Submitting...');
62
+ try {
63
+ const formData = new FormData(form);
64
+ const response = await fetch(action, {
65
+ method,
66
+ body: formData,
67
+ headers: { 'Accept': 'application/json' },
68
+ });
69
+ if (response.ok) {
70
+ showStatus('success', successMsg);
71
+ form.reset();
72
+ }
73
+ else {
74
+ showStatus('error', errorMsg);
75
+ }
76
+ }
77
+ catch {
78
+ showStatus('error', errorMsg);
79
+ }
80
+ };
81
+ form.addEventListener('submit', onSubmit);
82
+ return () => {
83
+ form.removeEventListener('submit', onSubmit);
84
+ honeypotDiv?.remove();
85
+ statusEl.remove();
86
+ };
87
+ }
88
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/behaviors/form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { CleanupFn } from '../types.js';
2
+ /**
3
+ * Preview behavior for `[data-rune="preview"]`.
4
+ *
5
+ * Creates a toolbar with:
6
+ * - View toggle: switch between preview and source code views
7
+ * - Viewport toggle: responsive viewport presets (mobile, tablet, desktop)
8
+ * - Theme toggle: auto, light, dark theme modes
9
+ *
10
+ * Source panel supports up to three tabs:
11
+ * - Markdoc: original authoring syntax (from source property)
12
+ * - Rune: pre-engine structural HTML (from htmlSource property)
13
+ * - HTML: post-engine themed HTML (from themedSource property, generated at build time)
14
+ *
15
+ * Reads configuration from data attributes:
16
+ * - `data-theme`: initial theme mode (default: 'auto')
17
+ * - `data-responsive`: comma-separated viewport presets
18
+ */
19
+ export declare function previewBehavior(el: HTMLElement): CleanupFn;
20
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/behaviors/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAwC7C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,CAwS1D"}