@neuralfog/elemix 0.1.8 → 0.2.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.
package/README.md CHANGED
@@ -1 +1,356 @@
1
- WIP
1
+ # ⚡ Elemix
2
+
3
+ Reactive elements based on Custom Elements.
4
+
5
+ This is a personal project built for my own use. It is not open source and comes with no guarantees, warranties, or support.
6
+
7
+ ## Why?
8
+
9
+ Because I can. The goal is a lean approach that utilizes as much of the native web platform as possible, with absolute freedom in how the application is architected by the implementer.
10
+
11
+ ## Reactivity
12
+
13
+ There is no virtual DOM. Rendering is handled by [elemix-renderer](https://github.com/neuralfog/elemix-renderer) which performs direct DOM mutations. Elemix uses a pub/sub model built on JS proxies. State objects are wrapped in a `Proxy` that intercepts property mutations. When a value is set, the proxy notifies all subscribed components, triggering a re-render. Nested objects are lazily wrapped in proxies on access, so deep mutations are also reactive. Components subscribe automatically when using `@state()` or `signals`.
14
+
15
+ Render scheduling is batched via `setTimeout(0)` — multiple state mutations in the same synchronous frame are coalesced into a single render. Once a render is scheduled, further mutations are locked until the next microtask, preventing redundant DOM updates.
16
+
17
+ Collections (`Map`, `Set`, `WeakMap`, `WeakSet`) are not supported in reactive state.
18
+
19
+ ## Init
20
+
21
+ ```typescript
22
+ import { initApp } from '@neuralfog/elemix/app';
23
+ import { makeCssStylesheet } from '@neuralfog/elemix/utilities';
24
+
25
+ import reset from './styles/reset.scss?inline';
26
+
27
+ initApp({
28
+ baseStyles: [makeCssStylesheet(reset)],
29
+ entryPoint: () => import('./components/MainApp'),
30
+ });
31
+ ```
32
+
33
+ ## Hello World Component
34
+
35
+ ```typescript
36
+ import { Component } from '@neuralfog/elemix';
37
+ import { component } from '@neuralfog/elemix/decorators';
38
+ import { html, type Template } from '@neuralfog/elemix/types';
39
+
40
+ @component()
41
+ export class HelloWorld extends Component {
42
+ template = (): Template => {
43
+ return html`<h1>Hello World!</h1>`;
44
+ };
45
+ }
46
+ ```
47
+
48
+ ## Styles
49
+
50
+ ```typescript
51
+ import { Component, html, type Template } from '@neuralfog/elemix';
52
+ import { component } from '@neuralfog/elemix/decorators';
53
+
54
+ import css from './HelloWorld.scss?inline';
55
+
56
+ @component({ styles: [css] })
57
+ export class HelloWorld extends Component {
58
+ template(): Template {
59
+ return html`<h1>Hello World!</h1>`;
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Rendering
65
+
66
+ Renders are triggered implicitly by mutating `@state()` properties or subscribed signals. To trigger a render manually, call `this.render()`. This is useful when you want fine control over rendering without reactivity getting in the way.
67
+
68
+ ```typescript
69
+ @component()
70
+ export class UserCard extends Component {
71
+ private name = 'John';
72
+
73
+ updateName(name: string): void {
74
+ this.name = name;
75
+ this.render();
76
+ }
77
+
78
+ template(): Template {
79
+ return html`<p>${this.name}</p>`;
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Props
85
+
86
+ Primitive props are shallow-diffed and only trigger updates when the value changes. Object props are passed through reactively. Functions are assigned once.
87
+
88
+ ```typescript
89
+ import { Component, html, type Template } from '@neuralfog/elemix';
90
+ import { component } from '@neuralfog/elemix/decorators';
91
+
92
+ type Props = {
93
+ name: string;
94
+ age: number;
95
+ handler: () => void;
96
+ meta: { role: string; active: boolean };
97
+ };
98
+
99
+ @component()
100
+ export class UserCard extends Component<Props> {
101
+ template(): Template {
102
+ const { name, age, handler, meta } = this.props;
103
+
104
+ return html`<div>
105
+ <p>${name}</p>
106
+ <p>${age}</p>
107
+ <p>${meta.role}</p>
108
+ <button @click=${handler}>Click</button>
109
+ </div>`;
110
+ }
111
+ }
112
+ ```
113
+
114
+ ```typescript
115
+ html`<user-card
116
+ :name=${'John'}
117
+ :age=${25}
118
+ :handler=${() => console.log('clicked')}
119
+ :meta=${{ role: 'admin', active: true }}
120
+ />`;
121
+ ```
122
+
123
+ Object props can be freely mutated — proxies retain reactivity and trigger updates for all subscribed components.
124
+
125
+ ## State
126
+
127
+ Local state is managed via the `@state()` decorator. Mutating any property on the state object will automatically trigger a re-render.
128
+
129
+ ```typescript
130
+ import { Component, html, type Template } from '@neuralfog/elemix';
131
+ import { component, state } from '@neuralfog/elemix/decorators';
132
+
133
+ @component()
134
+ export class UserProfile extends Component {
135
+ @state()
136
+ state = { name: 'John', email: 'john@example.com', active: true };
137
+
138
+ template(): Template {
139
+ const { name, email, active } = this.state;
140
+
141
+ return html`<div>
142
+ <p>${name}</p>
143
+ <p>${email}</p>
144
+ <p>${active ? 'Online' : 'Offline'}</p>
145
+ </div>`;
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Events
151
+
152
+ Use the `@` prefix to bind native DOM events directly in templates.
153
+
154
+ ```typescript
155
+ @component()
156
+ export class Counter extends Component {
157
+ @state()
158
+ state = { count: 0 };
159
+
160
+ template(): Template {
161
+ return html`<div>
162
+ <p>${this.state.count}</p>
163
+ <button @click=${() => this.state.count++}>Increment</button>
164
+ <input @input=${(e: Event) => console.log((e.target as HTMLInputElement).value)} />
165
+ </div>`;
166
+ }
167
+ }
168
+ ```
169
+
170
+ ## Model Binding
171
+
172
+ Use the `~model` prefix for two-way binding on input elements. The bound value must be an object with a `value` property.
173
+
174
+ ```typescript
175
+ @component()
176
+ export class SearchBox extends Component {
177
+ @state()
178
+ state = { query: { value: '' } };
179
+
180
+ template(): Template {
181
+ const { query } = this.state;
182
+
183
+ return html`<div>
184
+ <input ~model=${query} />
185
+ <p>${query.value}</p>
186
+ </div>`;
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Ref
192
+
193
+ The `ref` helper creates a `{ value }` wrapper. Use `:ref` to capture a DOM element reference, or pass it to `~model` for two-way binding.
194
+
195
+ ```typescript
196
+ import { Component, html, type Template } from '@neuralfog/elemix';
197
+ import { component, state } from '@neuralfog/elemix/decorators';
198
+ import { ref } from '@neuralfog/elemix/utilities';
199
+
200
+ @component()
201
+ export class Form extends Component {
202
+ @state()
203
+ input = ref('');
204
+ heading = ref<HTMLHeadingElement>();
205
+
206
+ template(): Template {
207
+ return html`<div>
208
+ <h1 :ref=${this.heading}>Title</h1>
209
+ <input ~model=${this.input} />
210
+ <p>${this.input.value}</p>
211
+ </div>`;
212
+ }
213
+ }
214
+ ```
215
+
216
+ ## Signals
217
+
218
+ Signals are reactive stores shared across components. Define a signal as a standalone module and subscribe components via the `@component` decorator.
219
+
220
+ ### Definition
221
+
222
+ ```typescript
223
+ import { signal } from '@neuralfog/elemix/signal';
224
+
225
+ export const counter = signal({
226
+ count: 0,
227
+ label: 'Clicks',
228
+ });
229
+ ```
230
+
231
+ ### Usage
232
+
233
+ Subscribe to a signal by passing it to the `signals` array. Access its state via `signal.value` and mutate it directly to trigger re-renders in all subscribed components.
234
+
235
+ ```typescript
236
+ import { Component, html, type Template } from '@neuralfog/elemix';
237
+ import { component } from '@neuralfog/elemix/decorators';
238
+
239
+ import { counter } from '../signals/counter';
240
+
241
+ @component({ signals: [counter] })
242
+ export class CounterDisplay extends Component {
243
+ template(): Template {
244
+ const { count, label } = counter.value;
245
+
246
+ return html`<div>
247
+ <p>${label}: ${count}</p>
248
+ <button @click=${() => counter.value.count++}>Increment</button>
249
+ </div>`;
250
+ }
251
+ }
252
+ ```
253
+
254
+ ## Lifecycle Hooks
255
+
256
+ Components support four lifecycle hooks: `beforeMount`, `onMount`, `onRender`, and `onDispose`.
257
+
258
+ ```typescript
259
+ @component()
260
+ export class Dashboard extends Component {
261
+ beforeMount(): void {
262
+ // Called before initialization
263
+ }
264
+
265
+ onMount(): void {
266
+ // Called after first render
267
+ }
268
+
269
+ onRender(renderTriggers?: string[]): void {
270
+ // Called after each render with triggers that caused the update
271
+ }
272
+
273
+ onDispose(): void {
274
+ // Called when component is removed from DOM
275
+ }
276
+
277
+ template(): Template {
278
+ return html`<div>Dashboard</div>`;
279
+ }
280
+ }
281
+ ```
282
+
283
+ ## Conditionals
284
+
285
+ Use ternary expressions in templates.
286
+
287
+ ```typescript
288
+ html`${isActive ? html`<p>Active</p>` : html`<p>Inactive</p>`}`;
289
+ ```
290
+
291
+ ## Repeat
292
+
293
+ Use the `repeat` directive to render lists. Pass an optional key function for efficient diffing.
294
+
295
+ ```typescript
296
+ import { repeat } from '@neuralfog/elemix/directives';
297
+
298
+ @component()
299
+ export class UserList extends Component {
300
+ @state()
301
+ state = {
302
+ users: [
303
+ { id: '1', name: 'Alice' },
304
+ { id: '2', name: 'Bob' },
305
+ ],
306
+ };
307
+
308
+ template(): Template {
309
+ const { users } = this.state;
310
+
311
+ return html`<ul>
312
+ ${repeat(
313
+ users,
314
+ (user) => html`<li>${user.name}</li>`,
315
+ (user) => user.id,
316
+ )}
317
+ </ul>`;
318
+ }
319
+ }
320
+ ```
321
+
322
+ ## Direct Property Assignment
323
+
324
+ Use the `.` prefix to set properties directly on HTML elements.
325
+
326
+ ```typescript
327
+ html`<input .value=${'Hello'} />`;
328
+
329
+ html`<div .textContent=${'Updated text'} />`;
330
+ ```
331
+
332
+ ## Dynamic Classes
333
+
334
+ Use the `.class` prefix to bind classes dynamically. Accepts a string or an object mapping class names to booleans.
335
+
336
+ ```typescript
337
+ html`<div .class=${'active highlight'}>String</div>`;
338
+
339
+ html`<div .class=${{ active: isActive, disabled: isDisabled }}>Object</div>`;
340
+ ```
341
+
342
+ ## Bind Attributes
343
+
344
+ Use `.bind-attrs` to dynamically set multiple attributes from an object.
345
+
346
+ ```typescript
347
+ html`<div .bind-attrs=${{ 'data-id': '123', 'aria-label': 'Card' }}>Content</div>`;
348
+ ```
349
+
350
+ ## Bind Events
351
+
352
+ Use `.bind-events` to dynamically attach multiple event handlers from an object.
353
+
354
+ ```typescript
355
+ html`<div .bind-events=${{ click: onClick, input: onInput }}>Content</div>`;
356
+ ```
@@ -0,0 +1 @@
1
+ var e=class e{static config={};static mergeConfigs(t){e.config={...e.config,...t}}},t=t=>{e.mergeConfigs(t),e.config.entryPoint?.()};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return e}});
@@ -0,0 +1 @@
1
+ var e=class{renderTrigger;subscribers=new Set;proxy;proxySet=new WeakSet;get value(){return this.proxy}constructor(e,t){this.renderTrigger=t,this.proxy=this.create(e)}subscribe(e){return this.subscribers.has(e)||this.subscribers.add(e),this}unsubscribe(e){return this.subscribers.delete(e),this}create(e){let t=this;return new Proxy(e,{get(e,r){let i=e[r];if(n(i),typeof i==`object`&&i&&!t.proxySet.has(i)){let e=new Proxy(i,this);return t.proxySet.add(e),e}return i},set(e,r,i){return e[r]=i,n(e),t.notify(),!0}})}notify(){for(let e of this.subscribers)e.render(this.renderTrigger)}},t=`Reactive state does not support collections: Map, WeakMap, Set, WeakSet`,n=e=>{if(e instanceof Map||e instanceof WeakMap||e instanceof Set||e instanceof WeakSet)throw Error(t)};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return e}});
package/dist/app.js CHANGED
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var s=(t,i,n)=>i in t?g(t,i,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[i]=n;var c=(t,i,n)=>s(t,typeof i!="symbol"?i+"":i,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=class o{static mergeConfigs(i){o.config={...o.config,...i}}};c(o,"config",{});let e=o;const f=t=>{var i,n;e.mergeConfigs(t),(n=(i=e.config).entryPoint)==null||n.call(i)};exports.App=e;exports.initApp=f;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./App-tMVVu8H5.js");exports.App=e.t,exports.initApp=e.n;
@@ -0,0 +1 @@
1
+ var e=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return e}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./utilities-oGilfnVK.js"),u=require("./types-D5TcFW7H.js"),d=(s,e)=>{customElements.get(s)===void 0&&customElements.define(s,e)},b=s=>e=>{const t=i.camelToKebabCase(e.name),a=class extends e{constructor(){var r;if(super(),(r=s==null?void 0:s.signals)!=null&&r.length)for(const l of s.signals)l.subscribe(this)}};e.$signals=(s==null?void 0:s.signals)||[],e.$styles=(s==null?void 0:s.styles)||[],d(t,a)},T=s=>(e,t)=>{e.stateProperties||(e.stateProperties=new Map),e.stateProperties.has(t)||e.stateProperties.set(t,s||u.RenderTrigger.LOCAL_STATE)};exports.component=b;exports.state=T;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types-v1ChTfwQ.js"),t=require("./utilities-BN8G-efH.js");var n=(e,t)=>{customElements.get(e)===void 0&&customElements.define(e,t)},r=e=>r=>{let i=t.t(r.name),a=class extends r{constructor(){if(super(),e?.signals?.length)for(let t of e.signals)t.subscribe(this)}};r.$signals=e?.signals||[],r.$styles=e?.styles||[],n(i,a)},i=t=>(n,r)=>{n.stateProperties||=new Map,n.stateProperties.has(r)||n.stateProperties.set(r,t||e.t.LOCAL_STATE)};exports.component=r,exports.state=i;
@@ -1,2 +1,2 @@
1
- import { repeat, condition } from '@neuralfog/elemix-renderer/directives';
2
- export { repeat, condition };
1
+ import { repeat } from '@neuralfog/elemix-renderer/directives';
2
+ export { repeat };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var o={},a;function l(){return a||(a=1,function(t){Object.defineProperty(t,Symbol.toStringTag,{value:"Module"});const v=(e,r,i)=>e.map((c,n)=>{const u=r(c,n);return u.key=(i==null?void 0:i(c,n))||String(n),u}),s=(e,r,i)=>(typeof e=="function"?e():e)?r:i;t.condition=s,t.repeat=v}(o)),o}var d=l();exports.condition=d.condition;exports.repeat=d.repeat;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=require("./chunk-NGjzN6Tu.js").t((e=>{Object.defineProperty(e,Symbol.toStringTag,{value:`Module`}),e.repeat=(e,t,n)=>e.map((e,r)=>{let i=t(e,r);return i.key=n?.(e,r)||String(r),i})}))();exports.repeat=e.repeat;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var h=Object.defineProperty;var a=(o,e,t)=>e in o?h(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var s=(o,e,t)=>a(o,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./types-D5TcFW7H.js"),d=require("@neuralfog/elemix-renderer"),n=require("./renderers-CTogHRY2.js"),l=require("./reactive.js"),p=require("./app.js");class u{constructor(e){s(this,"locked",!1);s(this,"scheduledRenderTriggers",new Set);this.component=e}schedule(e,t=!1){this.component.template()&&(e&&this.scheduledRenderTriggers.add(e),this.locked||(this.locked=!0,n.activeRenderers.add(this),setTimeout(()=>{this.render(Array.from(this.scheduledRenderTriggers)),this.scheduledRenderTriggers.clear(),this.locked=!1,n.activeRenderers.delete(this),t&&this.component.onMount()},0)))}render(e){d.render(this.component.template(),this.component.root),this.component.onRender(e)}}class m{constructor(e){this.component=e}initialize(){const e=this.component.constructor.prototype.stateProperties;if(e)for(const[t,r]of e){const c=this.component;c[t]=new l.Reactive(c[t],r).subscribe(this.component).value}}}class g{constructor(e){s(this,"data",{});this.component=e}initialize(){this.data=new l.Reactive(this.data,i.RenderTrigger.PROPS).subscribe(this.component).value}setReactive(e,t){this.data[e]=t}set(e,t){if(typeof t=="object"&&t!==null){this.setReactive(e,t);return}const r=this.data[e];if(typeof t=="function"){this.data[e]||this.setReactive(e,t);return}r!==t&&this.setReactive(e,t)}}class y{constructor(e){s(this,"styles");this.component=e,this.styles=this.component.constructor.$styles}initialize(){if(this.component.shadowRoot&&this.styles.length){const e=new CSSStyleSheet;e.replaceSync(this.styles.join(" "));const t=p.App.config.baseStyles||[];this.component.shadowRoot.adoptedStyleSheets=[...t,e,...this.component.controlStyles]}}}class f{constructor(){s(this,"data",{})}set(e,t){this.data[e]=t}}class S extends HTMLElement{constructor(){super();s(this,"$props",new g(this));s(this,"$emits",new f);s(this,"$renderer",new u(this));s(this,"$localState",new m(this));s(this,"$styles",new y(this));s(this,"$controlStyles");this.attachShadow({mode:"open"})}get root(){return this.shadowRoot}get props(){return this.$props.data}get emits(){return this.$emits.data}get styles(){return this.$styles}get controlStyles(){return this.$controlStyles||[]}connectedCallback(){this.beforeMount(),this.$styles.initialize(),this.$props.initialize(),this.$localState.initialize(),this.render(i.RenderTrigger.ON_MOUNT,!0)}disconnectedCallback(){n.activeRenderers.delete(this.$renderer),this.unsubscribeFromSignals(),this.onDispose()}template(){}onRender(t){}beforeMount(){}onMount(){}onDispose(){}render(t,r=!1){this.$renderer.schedule(t,r)}setControlStyles(t){this.$controlStyles=t}unsubscribeFromSignals(){for(const t of this.constructor.$signals)t.unsubscribe(this)}}exports.RenderTrigger=i.RenderTrigger;exports.html=i.html;exports.Component=S;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types-v1ChTfwQ.js"),t=require("./renderers-DuXHBLlK.js"),n=require("./Reactive-BR9C38bG.js"),r=require("./App-tMVVu8H5.js");let i=require("@neuralfog/elemix-renderer");var a=class{component;locked=!1;scheduledRenderTriggers=new Set;constructor(e){this.component=e}schedule(e,n=!1){this.component.template()&&(e&&this.scheduledRenderTriggers.add(e),this.locked||(this.locked=!0,t.t.add(this),setTimeout(()=>{this.render(Array.from(this.scheduledRenderTriggers)),this.scheduledRenderTriggers.clear(),this.locked=!1,t.t.delete(this),n&&this.component.onMount()},0)))}render(e){(0,i.render)(this.component.template(),this.component.root),this.component.onRender(e)}},o=class{component;constructor(e){this.component=e}initialize(){let e=this.component.constructor.prototype.stateProperties;if(e)for(let[t,r]of e){let e=this.component;e[t]=new n.t(e[t],r).subscribe(this.component).value}}},s=class{component;data={};constructor(e){this.component=e}initialize(){this.data=new n.t(this.data,e.t.PROPS).subscribe(this.component).value}setReactive(e,t){this.data[e]=t}set(e,t){if(typeof t==`object`&&t){this.setReactive(e,t);return}let n=this.data[e];if(typeof t==`function`){this.data[e]||this.setReactive(e,t);return}n!==t&&this.setReactive(e,t)}},c=class{component;styles;constructor(e){this.component=e,this.styles=this.component.constructor.$styles}initialize(){if(this.component.shadowRoot&&this.styles.length){let e=new CSSStyleSheet;e.replaceSync(this.styles.join(` `));let t=r.t.config.baseStyles||[];this.component.shadowRoot.adoptedStyleSheets=[...t,e,...this.component.controlStyles]}}},l=class extends HTMLElement{$props=new s(this);$renderer=new a(this);$localState=new o(this);$styles=new c(this);$controlStyles;get root(){return this.shadowRoot}get props(){return this.$props.data}get styles(){return this.$styles}get controlStyles(){return this.$controlStyles||[]}constructor(){super(),this.attachShadow({mode:`open`})}connectedCallback(){this.beforeMount(),this.$styles.initialize(),this.$props.initialize(),this.$localState.initialize(),this.render(e.t.ON_MOUNT,!0)}disconnectedCallback(){t.t.delete(this.$renderer),this.unsubscribeFromSignals(),this.onDispose()}template(){}onRender(e){}beforeMount(){}onMount(){}onDispose(){}render(e,t=!1){this.$renderer.schedule(e,t)}setControlStyles(e){this.$controlStyles=e}unsubscribeFromSignals(){for(let e of this.constructor.$signals)e.unsubscribe(this)}};exports.Component=l,exports.RenderTrigger=e.t,exports.html=e.n;
package/dist/reactive.js CHANGED
@@ -1 +1 @@
1
- "use strict";var b=Object.defineProperty;var h=(r,e,t)=>e in r?b(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var n=(r,e,t)=>h(r,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class S{constructor(e,t){n(this,"subscribers",new Set);n(this,"proxy");n(this,"proxySet",new WeakSet);this.renderTrigger=t,this.proxy=this.create(e)}get value(){return this.proxy}subscribe(e){return this.subscribers.has(e)||this.subscribers.add(e),this}unsubscribe(e){return this.subscribers.delete(e),this}create(e){const t=this;return new Proxy(e,{get(i,o){const s=i[o];if(u(s),typeof s=="object"&&s!==null&&!t.proxySet.has(s)){const c=new Proxy(s,this);return t.proxySet.add(c),c}return s},set(i,o,s){return i[o]=s,u(i),Array.isArray(i)&&o==="length",t.notify(),!0}})}notify(){for(const e of this.subscribers)e.render(this.renderTrigger)}}const a="Reactive state does not support collections: Map, WeakMap, Set, WeakSet",u=r=>{if(r instanceof Map||r instanceof WeakMap||r instanceof Set||r instanceof WeakSet)throw new Error(a)};exports.Reactive=S;exports.UNSUPPORTED_COLLECTION_ERROR_MESSAGE=a;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./Reactive-BR9C38bG.js");exports.Reactive=e.t,exports.UNSUPPORTED_COLLECTION_ERROR_MESSAGE=e.n;
@@ -0,0 +1 @@
1
+ var e=new Set;Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return e}});
package/dist/signal.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./reactive.js"),i=require("./types-D5TcFW7H.js"),n=(e,r)=>new t.Reactive(e,r||i.RenderTrigger.SIGNAL);exports.signal=n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./types-v1ChTfwQ.js"),t=require("./Reactive-BR9C38bG.js");var n=(n,r)=>new t.t(n,r||e.t.SIGNAL);exports.signal=n;
@@ -2,7 +2,7 @@ import type { Component } from './component/Component';
2
2
  import type { RenderTriggerType } from './types';
3
3
  export declare class Reactive<State> {
4
4
  private renderTrigger?;
5
- subscribers: Set<Component<unknown, unknown>>;
5
+ subscribers: Set<Component<unknown>>;
6
6
  private proxy;
7
7
  private proxySet;
8
8
  get value(): State;
@@ -1,15 +1,13 @@
1
1
  import { type RenderTriggerType, type Template } from '../types';
2
2
  import { Styles } from './Styles';
3
- export declare class Component<ComponentProps = unknown, ComponentEmits = unknown> extends HTMLElement {
3
+ export declare class Component<ComponentProps = unknown> extends HTMLElement {
4
4
  private $props;
5
- private $emits;
6
5
  private $renderer;
7
6
  private $localState;
8
7
  private $styles;
9
8
  private $controlStyles?;
10
9
  get root(): HTMLElement | ShadowRoot | null;
11
10
  get props(): ComponentProps;
12
- get emits(): ComponentEmits;
13
11
  get styles(): Styles;
14
12
  get controlStyles(): CSSStyleSheet[];
15
13
  constructor();
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- import '../fixtures/renderer/ConditionalRendering.ts';
1
+ import '../fixtures/renderer/ConditionalRendering';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- import '../fixtures/renderer/MultilpleRootNodes.ts';
1
+ import '../fixtures/renderer/MultilpleRootNodes';
@@ -0,0 +1 @@
1
+ var e=require("@neuralfog/elemix-renderer").html,t={PROPS:`PROPS`,SIGNAL:`SIGNAL`,LOCAL_STATE:`LOCAL_STATE`,ON_MOUNT:`ON_MOUNT`};Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return t}});
@@ -0,0 +1 @@
1
+ const e=require("./renderers-DuXHBLlK.js");var t=(e=void 0)=>({value:e}),n=()=>Math.floor(performance.now()*1e3).toString(36)+Math.random().toString(36).slice(2,6);function r(){return new Promise(t=>{let n=()=>{e.t.size===0?t(!1):setTimeout(n,0)};n()})}var i=e=>(e.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|[0-9]*$)|[A-Z]?[a-z]+|[A-Z]|[0-9]+/g)||[]).map(e=>e.toLowerCase()).join(`-`),a=e=>{let t=new CSSStyleSheet;return t.replaceSync(e),t};Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return i}});
package/dist/utilities.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./utilities-oGilfnVK.js");var o={},r={},n;function f(){if(n)return r;n=1;const s="";var l=(e=>(e[e.EVENT=0]="EVENT",e[e.PROP=1]="PROP",e[e.MODEL=2]="MODEL",e[e.STD=3]="STD",e[e.REF=4]="REF",e[e.EMIT=5]="EMIT",e[e.BIND_ATTRS=6]="BIND_ATTRS",e[e.BIND_EVENTS=7]="BIND_EVENTS",e[e.DIRECT_CLASS=8]="DIRECT_CLASS",e))(l||{});const m=e=>{const t=new RegExp(`${s}(\\d+)`),a=e.match(t);if(!a)throw new Error("Unable to extract index from hole comment");return Number(a[1])},E=e=>`<!--${s}${e}-->`,T=e=>e.replace(/<([a-zA-Z][^\s/>]*)([\s\S]*?)\/>/g,(t,a,R)=>a.includes("-")?`<${a}${R}></${a}>`:t),c=e=>e.replace(/(\S+)=((<!--[\s\S]*?-->)|([^\s">]+))/g,'$1="$2"'),S=e=>e.replace(/([A-Z])/g,t=>"-"+t.toLowerCase()),C=(e,t)=>Array.from(new Set([...e.split(" "),...t.split(" ")].filter(Boolean))).join(" ").trim();return r.Attributes=l,r.TEMPLATE_MARKER_GLYPH=s,r.camelToKebab=S,r.fixAttributeQuotes=c,r.fixSelfClosingTags=T,r.getIndexFromComment=m,r.makeMarkerComment=E,r.mergeClasses=C,r}var u;function g(){return u||(u=1,function(s){Object.defineProperty(s,Symbol.toStringTag,{value:"Module"});const l=f();s.mergeClasses=l.mergeClasses}(o)),o}var b=g();exports.camelToKebabCase=i.camelToKebabCase;exports.fastUID=i.fastUID;exports.makeCssStylesheet=i.makeCssStylesheet;exports.ref=i.ref;exports.render=i.render;exports.mergeClasses=b.mergeClasses;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunk-NGjzN6Tu.js"),t=require("./utilities-BN8G-efH.js");var n=e.t((e=>{var t={EVENT:0,PROP:1,MODEL:2,STD:3,REF:4,BIND_ATTRS:5,BIND_EVENTS:6,DIRECT_CLASS:7},n=RegExp(`₥(\\d+)`),r=e=>{let t=e.match(n);if(!t)throw Error(`Unable to extract index from hole comment`);return Number(t[1])},i=e=>`<!--₥${e}-->`,a=e=>e.replace(/(\S+)=((<!--[\s\S]*?-->)|([^\s">]+))/g,`$1="$2"`),o=e=>e.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`),s=(e,t)=>{let n=new Set,r=``,i=e=>{let t=e.split(` `);for(let e=0;e<t.length;e++){let i=t[e];i&&!n.has(i)&&(n.add(i),r.length&&(r+=` `),r+=i)}};return i(e),i(t),r};Object.defineProperty(e,"a",{enumerable:!0,get:function(){return s}}),Object.defineProperty(e,"i",{enumerable:!0,get:function(){return i}}),Object.defineProperty(e,"n",{enumerable:!0,get:function(){return a}}),Object.defineProperty(e,"o",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"r",{enumerable:!0,get:function(){return r}}),Object.defineProperty(e,"t",{enumerable:!0,get:function(){return o}})})),r=e.t((e=>{Object.defineProperty(e,Symbol.toStringTag,{value:`Module`}),e.mergeClasses=n().a}))();exports.camelToKebabCase=t.t,exports.fastUID=t.n,exports.makeCssStylesheet=t.r,Object.defineProperty(exports,"mergeClasses",{enumerable:!0,get:function(){return r.mergeClasses}}),exports.ref=t.i,exports.render=t.a;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuralfog/elemix",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "author": "brownhounds",
6
6
  "main": "dist/index.js",
@@ -26,20 +26,21 @@
26
26
  "test-nc": "vitest run",
27
27
  "test-ui": "npx vitest --api.host 0.0.0.0 --ui",
28
28
  "lint": "tsc --noEmit && biome format && biome lint",
29
+ "lint:fix": "biome format --write && biome lint --fix",
29
30
  "release": "npm run clean && npm run build && npm publish --access public"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@neuralfog/biome-config": "0.1.2",
33
- "@neuralfog/elemix-testing": "0.1.4",
34
- "@neuralfog/ts-config": "0.1.1",
35
- "@vitest/coverage-v8": "3.0.7",
36
- "@vitest/ui": "3.0.7",
37
- "jsdom": "26.0.0",
38
- "typescript": "5.8.2",
39
- "vite": "6.2.3",
40
- "vitest": "3.0.7"
34
+ "@neuralfog/elemix-testing": "0.2.0",
35
+ "@neuralfog/ts-config": "0.1.2",
36
+ "@vitest/coverage-v8": "4.1.7",
37
+ "@vitest/ui": "4.1.7",
38
+ "jsdom": "29.1.1",
39
+ "typescript": "6.0.3",
40
+ "vite": "8.0.14",
41
+ "vitest": "4.1.7"
41
42
  },
42
43
  "peerDependencies": {
43
- "@neuralfog/elemix-renderer": "0.1.8"
44
+ "@neuralfog/elemix-renderer": "0.3.0"
44
45
  }
45
46
  }
@@ -1 +0,0 @@
1
- "use strict";const e=new Set;exports.activeRenderers=e;
@@ -1,4 +0,0 @@
1
- export declare class Emits<ComponentEmits = unknown> {
2
- data: ComponentEmits;
3
- set(name: string, value: unknown): void;
4
- }
@@ -1,5 +0,0 @@
1
- import { Component } from '../../../src/component/Component';
2
- import { type Template } from '../../../src/types';
3
- export declare class SelfClosed extends Component {
4
- template: () => Template;
5
- }
@@ -1,16 +0,0 @@
1
- import { Component } from '../../../src/component/Component';
2
- import { type Template } from '../../../src/types';
3
- import './SelfClosed';
4
- export declare enum State {
5
- SINGLE = 0,
6
- START = 1,
7
- END = 2,
8
- MIDDLE = 3
9
- }
10
- export declare class SelfClosingTags extends Component {
11
- state: {
12
- case: State;
13
- };
14
- partial: () => Template;
15
- template: () => Template;
16
- }
@@ -1 +0,0 @@
1
- import '../fixtures/renderer/SelfClosingTags';
@@ -1 +0,0 @@
1
- "use strict";const e=require("@neuralfog/elemix-renderer"),r=e.html,t={PROPS:"PROPS",SIGNAL:"SIGNAL",LOCAL_STATE:"LOCAL_STATE",ON_MOUNT:"ON_MOUNT"};exports.RenderTrigger=t;exports.html=r;
@@ -1 +0,0 @@
1
- "use strict";const r=require("./renderers-CTogHRY2.js"),s=(e=void 0)=>({value:e}),n=()=>Math.floor(performance.now()*1e3).toString(36)+Math.random().toString(36).slice(2,6);function o(){return new Promise(e=>{const t=()=>{r.activeRenderers.size===0?e(!1):setTimeout(t,0)};t()})}const a=e=>(e.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|[0-9]*$)|[A-Z]?[a-z]+|[A-Z]|[0-9]+/g)||[]).map(t=>t.toLowerCase()).join("-"),c=e=>{const t=new CSSStyleSheet;return t.replaceSync(e),t};exports.camelToKebabCase=a;exports.fastUID=n;exports.makeCssStylesheet=c;exports.ref=s;exports.render=o;