thunderous 0.0.4 → 0.1.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
@@ -35,20 +35,16 @@ const myStyleSheet = css`
35
35
  `;
36
36
 
37
37
  const MyElement = customElement((params) => {
38
- const { connectedCallback, refs, adoptStyleSheet } = params;
38
+ const { customCallback, refs, adoptStyleSheet } = params;
39
39
 
40
40
  const [count, setCount] = createSignal(0);
41
41
 
42
- connectedCallback(() => {
43
- refs.increment.addEventListener('click', () => {
44
- setCount(count() + 1);
45
- });
46
- });
42
+ const increment = customCallback(() => setCount(count() + 1));
47
43
 
48
44
  adoptStyleSheet(myStyleSheet);
49
45
 
50
46
  return html`
51
- <button ref="increment">Increment</button>
47
+ <button onclick="${increment}">Increment</button>
52
48
  <output>${count}</output>
53
49
  `;
54
50
  });
@@ -157,19 +153,15 @@ Creating signals should look pretty familiar to most modern developers.
157
153
 
158
154
  <!-- prettier-ignore-start -->
159
155
  ```ts
160
- import { createSignal, customElement } from 'thunderous';
161
-
162
- const MyElement = customElement(() => {
163
- const [count, setCount] = createSignal(0);
156
+ import { createSignal } from 'thunderous';
164
157
 
165
- console.log(count()); // 0
158
+ const [count, setCount] = createSignal(0);
166
159
 
167
- setCount(1);
160
+ console.log(count()); // 0
168
161
 
169
- console.log(count()) // 1
162
+ setCount(1);
170
163
 
171
- /* ... */
172
- });
164
+ console.log(count()) // 1
173
165
  ```
174
166
  <!-- prettier-ignore-end -->
175
167
 
@@ -223,33 +215,79 @@ Usage:
223
215
 
224
216
  > NOTICE: Since `attrSignals` is a `Proxy` object, _any_ property will return a signal and auto-bind it to the attribute it corresponds with.
225
217
 
218
+ ##### Derived Signals
219
+
220
+ If you want to calculate a value based on another signal's value, you should use the `derived()` function. This signal will trigger its subscribers each time the signals inside change.
221
+
222
+ ```ts
223
+ import { derived, createSignal } from 'thunderous';
224
+
225
+ const [count, setCount] = createSignal(0);
226
+
227
+ const timesTen = derived(() => count() * 10);
228
+
229
+ console.log(timesTen()); // 0
230
+
231
+ setCount(10);
232
+
233
+ console.log(timesTen()); // 100
234
+ ```
235
+
236
+ ##### Effects
237
+
238
+ To run a callback each time a signal is changed, use the `createEffect()` function. Any signal used inside will trigger the callback when they're changed.
239
+
240
+ ```ts
241
+ import { createEffect } from 'thunderous';
242
+
243
+ /* ... */
244
+
245
+ createEffect(() => {
246
+ console.log(count());
247
+ });
248
+ ```
249
+
226
250
  #### Refs
227
251
 
228
- Finally, the refs property exists for convenience to avoid manually querying the DOM. Since the DOM is only available after rendering, refs will only work in and after the `connectedCallback` method. This is the best place for event binding to occur.
252
+ The refs property exists for convenience to avoid manually querying the DOM. Since the DOM is only available after rendering, refs will only work in and after the `connectedCallback` method.
229
253
 
230
254
  <!-- prettier-ignore-start -->
231
255
  ```ts
232
256
  const MyElement = customElement((params) => {
233
257
  const { connectedCallback, refs } = params;
234
258
 
235
- const [count, setCount] = createSignal(0);
236
-
237
259
  connectedCallback(() => {
238
- refs.increment.addEventListener('click', () => {
239
- setCount(count() + 1);
240
- });
260
+ console.log(refs.heading.textContent); // hello world
241
261
  });
242
262
 
263
+ return html`<h2 ref="heading">hello world</h2>`;
264
+ });
265
+ ```
266
+ <!-- prettier-ignore-end -->
267
+
268
+ #### Event Binding
269
+
270
+ While you could bind events in the `connectedCallback()` with `refs.button.addEventListener('click', handleClick)` for example, it may be more convenient to register a custom callback and bind it to the template.
271
+
272
+ <!-- prettier-ignore-start -->
273
+ ```ts
274
+ const MyElement = customElement((params) => {
275
+ const { customCallback } = params;
276
+
277
+ const [count, setCount] = createSignal(0);
278
+
279
+ const increment = customCallback(() => setCount(count() + 1));
280
+
243
281
  return html`
244
- <button ref="increment">Increment</button>
282
+ <button onclick="${increment}">Increment</button>
245
283
  <output>${count}</output>
246
284
  `;
247
285
  });
248
-
249
- MyElement.define('my-element');
250
286
  ```
251
287
  <!-- prettier-ignore-end -->
252
288
 
289
+ > NOTICE: This uses the native HTML inline event-binding syntax. There is no special syntax for `on` attributes, because it simply renders a reference to `this.getRootNode().host` and extracts the callback from there.
290
+
253
291
  ### Defining Custom Elements
254
292
 
255
293
  The `customElement()` function allows you to author a web component, returning an `ElementResult` that has some helpful methods like `define()` and `eject()`.
package/dist/index.cjs CHANGED
@@ -93,6 +93,7 @@ var customElement = (render, options) => {
93
93
  #formDisabledCallbackFns = /* @__PURE__ */ new Set();
94
94
  #formResetCallbackFns = /* @__PURE__ */ new Set();
95
95
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
96
+ __customCallbackFns = /* @__PURE__ */ new Map();
96
97
  #shadowRoot = this.attachShadow({ mode: "closed" });
97
98
  #internals = this.attachInternals();
98
99
  #observer = new MutationObserver((mutations) => {
@@ -121,6 +122,11 @@ var customElement = (render, options) => {
121
122
  formDisabledCallback: (fn) => this.#formDisabledCallbackFns.add(fn),
122
123
  formResetCallback: (fn) => this.#formResetCallbackFns.add(fn),
123
124
  formStateRestoreCallback: (fn) => this.#formStateRestoreCallbackFns.add(fn),
125
+ customCallback: (fn) => {
126
+ const key = crypto.randomUUID();
127
+ this.__customCallbackFns.set(key, fn);
128
+ return `{{callback:${key}}}`;
129
+ },
124
130
  attrSignals: new Proxy(
125
131
  {},
126
132
  {
@@ -245,6 +251,7 @@ var html = (strings, ...values) => {
245
251
  innerHTML += string + String(value);
246
252
  });
247
253
  const fragment = parseFragment(innerHTML);
254
+ const callbackBindingRegex = /(\{\{callback:.+\}\})/;
248
255
  const signalBindingRegex = /(\{\{signal:.+\}\})/;
249
256
  const parseChildren = (element) => {
250
257
  for (const child of element.childNodes) {
@@ -280,6 +287,9 @@ var html = (strings, ...values) => {
280
287
  }
281
288
  child.setAttribute(attr.name, newText);
282
289
  });
290
+ } else if (callbackBindingRegex.test(attr.value)) {
291
+ const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
292
+ child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
283
293
  }
284
294
  }
285
295
  parseChildren(child);
package/dist/index.d.cts CHANGED
@@ -22,6 +22,7 @@ type RenderProps = {
22
22
  formDisabledCallback: (fn: () => void) => void;
23
23
  formResetCallback: (fn: () => void) => void;
24
24
  formStateRestoreCallback: (fn: () => void) => void;
25
+ customCallback: (fn: () => void) => `{{callback:${string}}}`;
25
26
  attrSignals: Record<string, Signal<string | null>>;
26
27
  refs: Record<string, HTMLElement | null>;
27
28
  adoptStyleSheet: (stylesheet: CSSStyleSheet) => void;
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ type RenderProps = {
22
22
  formDisabledCallback: (fn: () => void) => void;
23
23
  formResetCallback: (fn: () => void) => void;
24
24
  formStateRestoreCallback: (fn: () => void) => void;
25
+ customCallback: (fn: () => void) => `{{callback:${string}}}`;
25
26
  attrSignals: Record<string, Signal<string | null>>;
26
27
  refs: Record<string, HTMLElement | null>;
27
28
  adoptStyleSheet: (stylesheet: CSSStyleSheet) => void;
package/dist/index.js CHANGED
@@ -62,6 +62,7 @@ var customElement = (render, options) => {
62
62
  #formDisabledCallbackFns = /* @__PURE__ */ new Set();
63
63
  #formResetCallbackFns = /* @__PURE__ */ new Set();
64
64
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
65
+ __customCallbackFns = /* @__PURE__ */ new Map();
65
66
  #shadowRoot = this.attachShadow({ mode: "closed" });
66
67
  #internals = this.attachInternals();
67
68
  #observer = new MutationObserver((mutations) => {
@@ -90,6 +91,11 @@ var customElement = (render, options) => {
90
91
  formDisabledCallback: (fn) => this.#formDisabledCallbackFns.add(fn),
91
92
  formResetCallback: (fn) => this.#formResetCallbackFns.add(fn),
92
93
  formStateRestoreCallback: (fn) => this.#formStateRestoreCallbackFns.add(fn),
94
+ customCallback: (fn) => {
95
+ const key = crypto.randomUUID();
96
+ this.__customCallbackFns.set(key, fn);
97
+ return `{{callback:${key}}}`;
98
+ },
93
99
  attrSignals: new Proxy(
94
100
  {},
95
101
  {
@@ -214,6 +220,7 @@ var html = (strings, ...values) => {
214
220
  innerHTML += string + String(value);
215
221
  });
216
222
  const fragment = parseFragment(innerHTML);
223
+ const callbackBindingRegex = /(\{\{callback:.+\}\})/;
217
224
  const signalBindingRegex = /(\{\{signal:.+\}\})/;
218
225
  const parseChildren = (element) => {
219
226
  for (const child of element.childNodes) {
@@ -249,6 +256,9 @@ var html = (strings, ...values) => {
249
256
  }
250
257
  child.setAttribute(attr.name, newText);
251
258
  });
259
+ } else if (callbackBindingRegex.test(attr.value)) {
260
+ const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
261
+ child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
252
262
  }
253
263
  }
254
264
  parseChildren(child);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",