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 CHANGED
@@ -1,7 +1,9 @@
1
1
  # Solid Web Components
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/sol-components)](https://www.npmjs.com/package/sol-components)
4
+
3
5
  ## This is a Work In Progress!
4
6
 
5
- See the <a href="https://solidos.github.io/sol-components/">help pages</a> for details.
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.
@@ -118,8 +118,22 @@ function fillSelect(el, vars, rows) {
118
118
  }
119
119
  }
120
120
 
121
- // The host's tag picks the shape. Unknown tags render nothing the W3C JSON is
122
- // left on `el.swcData` for the page to consume.
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
- // anything else: leave the DOM untouched; the JSON is on el.swcData.
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); });
@@ -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 };
@@ -120,6 +120,7 @@
120
120
 
121
121
  "attributes": {
122
122
  "data-from-query": "sol-components/core/from-query.js",
123
+ "data-handler": "sol-components/core/handler.js",
123
124
  "data-edit-shape data-from-rdf": "rdf-bundle"
124
125
  },
125
126
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol-components",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Web components and Node.js tools for querying, including, and editing RDF/Linked Data on Solid Pods",
5
5
  "type": "module",
6
6
  "exports": {