sol-components 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/core/from-query.js +18 -3
- package/core/handler.js +88 -0
- package/dist/sol-loader.manifest.json +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Solid Web Components
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/sol-components)
|
|
4
|
+
|
|
3
5
|
## This is a Work In Progress!
|
|
4
6
|
|
|
5
|
-
See the <a href="https://
|
|
7
|
+
See the <a href="https://jeff-zucker.github.io/sol-components/">help pages</a> for details.
|
|
6
8
|
|
|
7
9
|
(c) 2023-2026, Jeff Zucker, may be freely distributed under an MIT or Apache license.
|
package/core/from-query.js
CHANGED
|
@@ -118,8 +118,22 @@ function fillSelect(el, vars, rows) {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
//
|
|
122
|
-
//
|
|
121
|
+
// While the query runs, show a loading indicator IN the host, shaped to its tag
|
|
122
|
+
// (a <li> in a list, an <option> in a select, else a <div>). aria-live announces it.
|
|
123
|
+
function setLoading(el) {
|
|
124
|
+
const tag = el.localName;
|
|
125
|
+
let node;
|
|
126
|
+
if (tag === 'ul' || tag === 'ol') node = document.createElement('li');
|
|
127
|
+
else if (tag === 'select') { node = document.createElement('option'); node.disabled = true; node.selected = true; }
|
|
128
|
+
else { node = document.createElement('div'); node.setAttribute('role', 'status'); }
|
|
129
|
+
node.className = 'swc-loading';
|
|
130
|
+
node.setAttribute('aria-live', 'polite');
|
|
131
|
+
node.textContent = 'Loading…';
|
|
132
|
+
el.replaceChildren(node);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The host's tag picks the shape. Unknown tags render nothing into the DOM — the
|
|
136
|
+
// W3C JSON is left on `el.swcData` for the page to consume.
|
|
123
137
|
function renderInto(el, data) {
|
|
124
138
|
el.swcData = data;
|
|
125
139
|
const vars = data.head.vars;
|
|
@@ -127,11 +141,12 @@ function renderInto(el, data) {
|
|
|
127
141
|
const tag = el.localName;
|
|
128
142
|
if (tag === 'ul' || tag === 'ol') return fillList(el, vars, rows);
|
|
129
143
|
if (tag === 'select') return fillSelect(el, vars, rows);
|
|
130
|
-
//
|
|
144
|
+
el.replaceChildren(); // clear the loading indicator; the JSON is on el.swcData
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
activate('[data-from-query]', (el) => {
|
|
134
148
|
if (el.localName === 'sol-query') return; // the element handles itself
|
|
149
|
+
setLoading(el);
|
|
135
150
|
buildData(el)
|
|
136
151
|
.then((data) => renderInto(el, data))
|
|
137
152
|
.catch((e) => { el.innerHTML = `<div class="error">${(e && e.message) || e}</div>`; console.error('[data-from-query]', e); });
|
package/core/handler.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// handler.js — the `handler` capability (part of component-interop).
|
|
2
|
+
//
|
|
3
|
+
// Lets ANY element (a library's own button/menu, a plain link) activate a
|
|
4
|
+
// component or a script, with NO display logic in component-interop: ci catches
|
|
5
|
+
// the activation, optionally instantiates a named component, forwards the
|
|
6
|
+
// element's data, and fires ONE event — the consuming app decides what happens
|
|
7
|
+
// and where any result goes.
|
|
8
|
+
//
|
|
9
|
+
// <a data-handler="my-viewer" href="report.ttl" data-mode="compact">Open</a> <!-- component -->
|
|
10
|
+
// <button data-handler="exportCsv" data-format="utf8">Export</button> <!-- script -->
|
|
11
|
+
//
|
|
12
|
+
// document.addEventListener('interop:activate', (e) => {
|
|
13
|
+
// const { handler, element, data, source } = e.detail;
|
|
14
|
+
// if (element) source.closest('.pane').querySelector('.output').replaceChildren(element);
|
|
15
|
+
// else if (handler === 'exportCsv') exportTheTable(data);
|
|
16
|
+
// });
|
|
17
|
+
//
|
|
18
|
+
// Zero imports. Activation is delegated at the document level, so dynamically
|
|
19
|
+
// added elements and shadow-DOM buttons (composed events) work with no setup.
|
|
20
|
+
|
|
21
|
+
// A custom-element tag (must contain a hyphen) or an already-registered element
|
|
22
|
+
// → instantiate a component; otherwise it's a bare-name action (script).
|
|
23
|
+
function isComponentTag(name) {
|
|
24
|
+
return !!name && (name.indexOf('-') !== -1 || !!customElements.get(name));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Collect the element's payload: href (if any) + data-* attributes with the
|
|
28
|
+
// `data-` prefix stripped, excluding `data-handler` itself.
|
|
29
|
+
function collectData(el) {
|
|
30
|
+
const data = {};
|
|
31
|
+
const href = el.getAttribute && el.getAttribute('href');
|
|
32
|
+
if (href != null) data.href = href;
|
|
33
|
+
const attrs = el.attributes || [];
|
|
34
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
35
|
+
const a = attrs[i];
|
|
36
|
+
if (a.name === 'data-handler') continue;
|
|
37
|
+
if (a.name.indexOf('data-') === 0) data[a.name.slice(5)] = a.value;
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Forward the payload onto the instantiated component as attributes
|
|
43
|
+
// (href → href, data-mode → mode, …). The component reads its own attributes.
|
|
44
|
+
function forwardAttrs(target, data) {
|
|
45
|
+
for (const key in data) {
|
|
46
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) target.setAttribute(key, data[key]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function activate(el) {
|
|
51
|
+
const name = el.getAttribute('data-handler');
|
|
52
|
+
if (!name) return;
|
|
53
|
+
const data = collectData(el);
|
|
54
|
+
|
|
55
|
+
let element = null;
|
|
56
|
+
if (isComponentTag(name)) {
|
|
57
|
+
element = document.createElement(name);
|
|
58
|
+
forwardAttrs(element, data);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
el.dispatchEvent(new CustomEvent('interop:activate', {
|
|
62
|
+
bubbles: true, composed: true,
|
|
63
|
+
detail: { handler: name, element: element, data: data, source: el },
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onClick(e) {
|
|
68
|
+
const el = e.target && e.target.closest && e.target.closest('[data-handler]');
|
|
69
|
+
if (!el) return;
|
|
70
|
+
e.preventDefault(); // don't navigate / submit — the app routes it
|
|
71
|
+
activate(el);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function onKeydown(e) {
|
|
75
|
+
if (e.key !== 'Enter' && e.key !== ' ' && e.key !== 'Spacebar') return;
|
|
76
|
+
const el = e.target && e.target.closest && e.target.closest('[data-handler]');
|
|
77
|
+
if (!el) return;
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
activate(el);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof document !== 'undefined' && !document.__ciHandlerWired) {
|
|
83
|
+
document.__ciHandlerWired = true;
|
|
84
|
+
document.addEventListener('click', onClick);
|
|
85
|
+
document.addEventListener('keydown', onKeydown);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { activate, isComponentTag, collectData };
|