@coherent.js/web-components 1.0.0-beta.6 → 1.0.0-beta.7

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/dist/index.js ADDED
@@ -0,0 +1,110 @@
1
+ // src/index.js
2
+ import { render } from "@coherent.js/core";
3
+ function defineComponent(name, component, options = {}) {
4
+ if (typeof window === "undefined") {
5
+ return { name, component, options };
6
+ }
7
+ if (!window.customElements) {
8
+ throw new Error("Custom Elements API not supported");
9
+ }
10
+ const observedAttrs = options.observedAttributes || [];
11
+ const defaults = options.defaults || {};
12
+ class CoherentElement extends HTMLElement {
13
+ static get observedAttributes() {
14
+ return observedAttrs;
15
+ }
16
+ constructor() {
17
+ super();
18
+ this._props = { ...defaults };
19
+ this._connected = false;
20
+ this.component = component;
21
+ this.options = options;
22
+ }
23
+ connectedCallback() {
24
+ this._connected = true;
25
+ for (const attr of observedAttrs) {
26
+ if (this.hasAttribute(attr)) {
27
+ this._props[attr] = this.getAttribute(attr);
28
+ }
29
+ }
30
+ this._render();
31
+ }
32
+ disconnectedCallback() {
33
+ this._connected = false;
34
+ if (options.shadow && this.shadowRoot) {
35
+ this.shadowRoot.innerHTML = "";
36
+ } else {
37
+ this.innerHTML = "";
38
+ }
39
+ }
40
+ attributeChangedCallback(attrName, oldValue, newValue) {
41
+ if (oldValue === newValue) return;
42
+ this._props[attrName] = newValue;
43
+ if (this._connected) {
44
+ this._render();
45
+ }
46
+ }
47
+ /**
48
+ * Set a property and re-render
49
+ */
50
+ setProperty(key, value) {
51
+ this._props[key] = value;
52
+ if (this._connected) {
53
+ this._render();
54
+ }
55
+ }
56
+ /**
57
+ * Get current props
58
+ */
59
+ getProperties() {
60
+ return { ...this._props };
61
+ }
62
+ _render() {
63
+ const componentDef = typeof this.component === "function" ? this.component(this._props) : this.component;
64
+ const html = render(componentDef);
65
+ if (this.options.shadow) {
66
+ if (!this.shadowRoot) {
67
+ this.attachShadow({ mode: "open" });
68
+ }
69
+ this.shadowRoot.innerHTML = html;
70
+ this._delegateEvents(this.shadowRoot);
71
+ } else {
72
+ this.innerHTML = html;
73
+ this._delegateEvents(this);
74
+ }
75
+ }
76
+ /**
77
+ * Delegate data-action events to component handlers
78
+ */
79
+ _delegateEvents(root) {
80
+ const actionElements = root.querySelectorAll("[data-action]");
81
+ for (const el of actionElements) {
82
+ const action = el.dataset.action;
83
+ const [eventType, handlerName] = action.includes(":") ? action.split(":") : ["click", action];
84
+ el.addEventListener(eventType, (event) => {
85
+ this.dispatchEvent(new CustomEvent("coherent-action", {
86
+ bubbles: true,
87
+ detail: { action: handlerName, event, element: el }
88
+ }));
89
+ });
90
+ }
91
+ }
92
+ }
93
+ window.customElements.define(name, CoherentElement);
94
+ return CoherentElement;
95
+ }
96
+ function integrateWithWebComponents(_runtime) {
97
+ return {
98
+ defineComponent: (name, component, options) => defineComponent(name, component, options)
99
+ };
100
+ }
101
+ function defineCoherentElement(name, component, options = {}) {
102
+ return defineComponent(name, component, options);
103
+ }
104
+ export {
105
+ defineComponent as default,
106
+ defineCoherentElement,
107
+ defineComponent,
108
+ integrateWithWebComponents
109
+ };
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.js"],
4
+ "sourcesContent": ["/**\n * Coherent.js Web Components Integration\n * Provides custom element and web component utilities\n */\n\nimport { render } from '@coherent.js/core';\n\n/**\n * Define a Coherent.js component as a custom element\n *\n * @param {string} name - Custom element tag name (must contain a hyphen)\n * @param {Function|Object} component - Coherent.js component (function or object)\n * @param {Object} [options] - Configuration options\n * @param {boolean} [options.shadow] - Use Shadow DOM for style encapsulation\n * @param {string[]} [options.observedAttributes] - Attributes to watch for changes\n * @param {Object} [options.defaults] - Default property values\n * @returns {Function|Object} The custom element class, or a server-side placeholder\n */\nexport function defineComponent(name, component, options = {}) {\n if (typeof window === 'undefined') {\n // Server-side: return a placeholder for SSR\n return { name, component, options };\n }\n\n if (!window.customElements) {\n throw new Error('Custom Elements API not supported');\n }\n\n const observedAttrs = options.observedAttributes || [];\n const defaults = options.defaults || {};\n\n class CoherentElement extends HTMLElement {\n static get observedAttributes() {\n return observedAttrs;\n }\n\n constructor() {\n super();\n this._props = { ...defaults };\n this._connected = false;\n this.component = component;\n this.options = options;\n }\n\n connectedCallback() {\n this._connected = true;\n // Copy initial attributes to props\n for (const attr of observedAttrs) {\n if (this.hasAttribute(attr)) {\n this._props[attr] = this.getAttribute(attr);\n }\n }\n this._render();\n }\n\n disconnectedCallback() {\n this._connected = false;\n // Cleanup: remove event listeners and clear content\n if (options.shadow && this.shadowRoot) {\n this.shadowRoot.innerHTML = '';\n } else {\n this.innerHTML = '';\n }\n }\n\n attributeChangedCallback(attrName, oldValue, newValue) {\n if (oldValue === newValue) return;\n this._props[attrName] = newValue;\n if (this._connected) {\n this._render();\n }\n }\n\n /**\n * Set a property and re-render\n */\n setProperty(key, value) {\n this._props[key] = value;\n if (this._connected) {\n this._render();\n }\n }\n\n /**\n * Get current props\n */\n getProperties() {\n return { ...this._props };\n }\n\n _render() {\n const componentDef = typeof this.component === 'function'\n ? this.component(this._props)\n : this.component;\n\n const html = render(componentDef);\n\n if (this.options.shadow) {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: 'open' });\n }\n this.shadowRoot.innerHTML = html;\n this._delegateEvents(this.shadowRoot);\n } else {\n this.innerHTML = html;\n this._delegateEvents(this);\n }\n }\n\n /**\n * Delegate data-action events to component handlers\n */\n _delegateEvents(root) {\n const actionElements = root.querySelectorAll('[data-action]');\n for (const el of actionElements) {\n const action = el.dataset.action;\n const [eventType, handlerName] = action.includes(':')\n ? action.split(':')\n : ['click', action];\n\n el.addEventListener(eventType, (event) => {\n this.dispatchEvent(new CustomEvent('coherent-action', {\n bubbles: true,\n detail: { action: handlerName, event, element: el }\n }));\n });\n }\n }\n }\n\n window.customElements.define(name, CoherentElement);\n return CoherentElement;\n}\n\n/**\n * Integration utilities for runtime environments\n */\nexport function integrateWithWebComponents(_runtime) {\n return {\n defineComponent: (name, component, options) => defineComponent(name, component, options)\n };\n}\n\n/**\n * Alias for defineComponent\n */\nexport function defineCoherentElement(name, component, options = {}) {\n return defineComponent(name, component, options);\n}\n\nexport { defineComponent as default };\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/web-components",
3
- "version": "1.0.0-beta.6",
3
+ "version": "1.0.0-beta.7",
4
4
  "description": "Web Components integration for Coherent.js",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -15,8 +15,8 @@
15
15
  ],
16
16
  "author": "Coherent.js Team",
17
17
  "license": "MIT",
18
- "dependencies": {
19
- "@coherent.js/core": "1.0.0-beta.6"
18
+ "peerDependencies": {
19
+ "@coherent.js/core": "1.0.0-beta.7"
20
20
  },
21
21
  "devDependencies": {
22
22
  "vitest": "^3.2.4"
@@ -25,11 +25,20 @@
25
25
  "type": "git",
26
26
  "url": "git+https://github.com/Tomdrouv1/coherent.js.git"
27
27
  },
28
+ "homepage": "https://github.com/Tomdrouv1/coherent.js",
29
+ "bugs": {
30
+ "url": "https://github.com/Tomdrouv1/coherent.js/issues"
31
+ },
28
32
  "publishConfig": {
29
33
  "access": "public"
30
34
  },
35
+ "engines": {
36
+ "node": ">=20.0.0"
37
+ },
31
38
  "types": "./types/index.d.ts",
32
39
  "files": [
40
+ "dist/",
41
+ "src/",
33
42
  "LICENSE",
34
43
  "README.md",
35
44
  "types/"
package/src/index.js CHANGED
@@ -7,10 +7,18 @@ import { render } from '@coherent.js/core';
7
7
 
8
8
  /**
9
9
  * Define a Coherent.js component as a custom element
10
+ *
11
+ * @param {string} name - Custom element tag name (must contain a hyphen)
12
+ * @param {Function|Object} component - Coherent.js component (function or object)
13
+ * @param {Object} [options] - Configuration options
14
+ * @param {boolean} [options.shadow] - Use Shadow DOM for style encapsulation
15
+ * @param {string[]} [options.observedAttributes] - Attributes to watch for changes
16
+ * @param {Object} [options.defaults] - Default property values
17
+ * @returns {Function|Object} The custom element class, or a server-side placeholder
10
18
  */
11
19
  export function defineComponent(name, component, options = {}) {
12
20
  if (typeof window === 'undefined') {
13
- // Server-side: just return a placeholder
21
+ // Server-side: return a placeholder for SSR
14
22
  return { name, component, options };
15
23
  }
16
24
 
@@ -18,24 +26,104 @@ export function defineComponent(name, component, options = {}) {
18
26
  throw new Error('Custom Elements API not supported');
19
27
  }
20
28
 
29
+ const observedAttrs = options.observedAttributes || [];
30
+ const defaults = options.defaults || {};
31
+
21
32
  class CoherentElement extends HTMLElement {
33
+ static get observedAttributes() {
34
+ return observedAttrs;
35
+ }
36
+
22
37
  constructor() {
23
38
  super();
39
+ this._props = { ...defaults };
40
+ this._connected = false;
24
41
  this.component = component;
25
42
  this.options = options;
26
43
  }
27
44
 
28
45
  connectedCallback() {
29
- this.render();
46
+ this._connected = true;
47
+ // Copy initial attributes to props
48
+ for (const attr of observedAttrs) {
49
+ if (this.hasAttribute(attr)) {
50
+ this._props[attr] = this.getAttribute(attr);
51
+ }
52
+ }
53
+ this._render();
54
+ }
55
+
56
+ disconnectedCallback() {
57
+ this._connected = false;
58
+ // Cleanup: remove event listeners and clear content
59
+ if (options.shadow && this.shadowRoot) {
60
+ this.shadowRoot.innerHTML = '';
61
+ } else {
62
+ this.innerHTML = '';
63
+ }
64
+ }
65
+
66
+ attributeChangedCallback(attrName, oldValue, newValue) {
67
+ if (oldValue === newValue) return;
68
+ this._props[attrName] = newValue;
69
+ if (this._connected) {
70
+ this._render();
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Set a property and re-render
76
+ */
77
+ setProperty(key, value) {
78
+ this._props[key] = value;
79
+ if (this._connected) {
80
+ this._render();
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get current props
86
+ */
87
+ getProperties() {
88
+ return { ...this._props };
30
89
  }
31
90
 
32
- render() {
33
- const html = render(this.component);
91
+ _render() {
92
+ const componentDef = typeof this.component === 'function'
93
+ ? this.component(this._props)
94
+ : this.component;
95
+
96
+ const html = render(componentDef);
97
+
34
98
  if (this.options.shadow) {
35
- const shadow = this.attachShadow({ mode: 'open' });
36
- shadow.innerHTML = html;
99
+ if (!this.shadowRoot) {
100
+ this.attachShadow({ mode: 'open' });
101
+ }
102
+ this.shadowRoot.innerHTML = html;
103
+ this._delegateEvents(this.shadowRoot);
37
104
  } else {
38
105
  this.innerHTML = html;
106
+ this._delegateEvents(this);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Delegate data-action events to component handlers
112
+ */
113
+ _delegateEvents(root) {
114
+ const actionElements = root.querySelectorAll('[data-action]');
115
+ for (const el of actionElements) {
116
+ const action = el.dataset.action;
117
+ const [eventType, handlerName] = action.includes(':')
118
+ ? action.split(':')
119
+ : ['click', action];
120
+
121
+ el.addEventListener(eventType, (event) => {
122
+ this.dispatchEvent(new CustomEvent('coherent-action', {
123
+ bubbles: true,
124
+ detail: { action: handlerName, event, element: el }
125
+ }));
126
+ });
39
127
  }
40
128
  }
41
129
  }
@@ -45,7 +133,7 @@ export function defineComponent(name, component, options = {}) {
45
133
  }
46
134
 
47
135
  /**
48
- * Integration utilities
136
+ * Integration utilities for runtime environments
49
137
  */
50
138
  export function integrateWithWebComponents(_runtime) {
51
139
  return {
@@ -53,6 +141,9 @@ export function integrateWithWebComponents(_runtime) {
53
141
  };
54
142
  }
55
143
 
144
+ /**
145
+ * Alias for defineComponent
146
+ */
56
147
  export function defineCoherentElement(name, component, options = {}) {
57
148
  return defineComponent(name, component, options);
58
149
  }