@simpreact/simpreact 0.0.1 → 0.0.3

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.
Files changed (65) hide show
  1. package/compat/core.d.ts +46 -0
  2. package/compat/core.js +93 -0
  3. package/compat/dom.d.ts +11 -0
  4. package/compat/dom.js +10 -0
  5. package/compat/hooks.d.ts +26 -0
  6. package/compat/hooks.js +77 -0
  7. package/compat/index.d.ts +42 -0
  8. package/compat/index.js +14 -0
  9. package/compat/jsx-runtime.d.ts +10 -0
  10. package/compat/jsx-runtime.js +9 -0
  11. package/compat/utils.d.ts +1 -0
  12. package/compat/utils.js +3 -0
  13. package/core/context.d.ts +1 -1
  14. package/core/createElement.d.ts +4 -4
  15. package/core/createElement.js +13 -6
  16. package/core/fragment.d.ts +1 -1
  17. package/core/hostAdapter.d.ts +3 -3
  18. package/core/index.d.ts +3 -3
  19. package/core/index.js +4 -4
  20. package/core/internal.d.ts +11 -11
  21. package/core/internal.js +11 -11
  22. package/core/lifecycleEventBus.d.ts +6 -2
  23. package/core/lifecycleEventBus.js +1 -1
  24. package/core/mounting.d.ts +4 -4
  25. package/core/mounting.js +42 -11
  26. package/core/patching.d.ts +4 -4
  27. package/core/patching.js +89 -19
  28. package/core/portal.d.ts +1 -1
  29. package/core/portal.js +1 -1
  30. package/core/ref.d.ts +5 -5
  31. package/core/rerender.d.ts +5 -5
  32. package/core/rerender.js +38 -25
  33. package/core/unmounting.d.ts +3 -3
  34. package/core/unmounting.js +7 -3
  35. package/dom/attach-element-to-dom.d.ts +1 -1
  36. package/dom/attach-element-to-dom.js +1 -1
  37. package/dom/domAdapter.d.ts +1 -1
  38. package/dom/domAdapter.js +10 -5
  39. package/dom/events.d.ts +4 -1
  40. package/dom/events.js +10 -4
  41. package/dom/index.d.ts +2 -2
  42. package/dom/index.js +2 -2
  43. package/dom/props/controlled/index.d.ts +1 -1
  44. package/dom/props/controlled/index.js +3 -3
  45. package/dom/props/controlled/input.d.ts +1 -1
  46. package/dom/props/controlled/input.js +6 -6
  47. package/dom/props/controlled/select.d.ts +1 -1
  48. package/dom/props/controlled/select.js +5 -5
  49. package/dom/props/controlled/textarea.d.ts +1 -1
  50. package/dom/props/controlled/textarea.js +6 -6
  51. package/dom/props/dangerInnerHTML.d.ts +1 -1
  52. package/dom/props/index.d.ts +1 -1
  53. package/dom/props/index.js +1 -1
  54. package/dom/props/props.d.ts +1 -1
  55. package/dom/props/props.js +6 -6
  56. package/dom/props/style.js +6 -3
  57. package/dom/render.d.ts +1 -1
  58. package/dom/render.js +5 -5
  59. package/hooks/index.d.ts +7 -1
  60. package/hooks/index.js +43 -10
  61. package/jsx-runtime/index.d.ts +3 -3
  62. package/package.json +8 -1
  63. package/shared/index.js +3 -3
  64. package/shared/utils.d.ts +2 -1
  65. package/shared/utils.js +16 -0
@@ -1,5 +1,5 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
2
- import { getElementFromDom } from '../../attach-element-to-dom';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
+ import { getElementFromDom } from '../../attach-element-to-dom.js';
3
3
  export function isCheckedType(type) {
4
4
  return type === 'checkbox' || type === 'radio';
5
5
  }
@@ -12,9 +12,9 @@ function onControlledInputInput(event) {
12
12
  return;
13
13
  }
14
14
  if (element.props['onInput']) {
15
- syncRerenderLocker.lock();
15
+ batchingRerenderLocker.lock();
16
16
  element.props['onInput'](event);
17
- syncRerenderLocker.flush();
17
+ batchingRerenderLocker.flush();
18
18
  element = getElementFromDom(event.target);
19
19
  }
20
20
  if (element) {
@@ -27,9 +27,9 @@ function onControlledInputChange(event) {
27
27
  return;
28
28
  }
29
29
  if (element.props['onChange']) {
30
- syncRerenderLocker.lock();
30
+ batchingRerenderLocker.lock();
31
31
  element.props['onChange'](event);
32
- syncRerenderLocker.flush();
32
+ batchingRerenderLocker.flush();
33
33
  element = getElementFromDom(event.target);
34
34
  }
35
35
  if (element) {
@@ -1,5 +1,5 @@
1
1
  import type { SimpElement } from '../../../core/internal.js';
2
- import type { Dict } from '../../../shared';
2
+ import type { Dict } from '../../../shared/index.js';
3
3
  export declare function isEventNameIgnored(eventName: string): boolean;
4
4
  export declare function addControlledSelectEventHandlers(dom: HTMLSelectElement): void;
5
5
  export declare function removeControlledSelectEventHandlers(dom: HTMLSelectElement): void;
@@ -1,6 +1,6 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
2
- import { emptyObject } from '../../../shared';
3
- import { getElementFromDom } from '../../attach-element-to-dom';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
+ import { emptyObject } from '../../../shared/index.js';
3
+ import { getElementFromDom } from '../../attach-element-to-dom.js';
4
4
  export function isEventNameIgnored(eventName) {
5
5
  return eventName === 'onChange';
6
6
  }
@@ -10,9 +10,9 @@ function onControlledInputChange(event) {
10
10
  return;
11
11
  }
12
12
  if (element.props['onChange']) {
13
- syncRerenderLocker.lock();
13
+ batchingRerenderLocker.lock();
14
14
  element.props['onChange'](event);
15
- syncRerenderLocker.flush();
15
+ batchingRerenderLocker.flush();
16
16
  element = getElementFromDom(event.target);
17
17
  }
18
18
  if (element) {
@@ -1,5 +1,5 @@
1
1
  import type { SimpElement } from '../../../core/internal.js';
2
- import type { Dict } from '../../../shared';
2
+ import type { Dict } from '../../../shared/index.js';
3
3
  export declare function isEventNameIgnored(eventName: string): boolean;
4
4
  export declare function addControlledTextareaEventHandlers(dom: HTMLTextAreaElement): void;
5
5
  export declare function removeControlledTextareaEventHandlers(dom: HTMLTextAreaElement): void;
@@ -1,5 +1,5 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
2
- import { getElementFromDom } from '../../attach-element-to-dom';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
+ import { getElementFromDom } from '../../attach-element-to-dom.js';
3
3
  export function isEventNameIgnored(eventName) {
4
4
  return eventName === 'onChange' || eventName === 'onInput';
5
5
  }
@@ -9,9 +9,9 @@ function onControlledTextareaChange(event) {
9
9
  return;
10
10
  }
11
11
  if (element.props['onChange']) {
12
- syncRerenderLocker.lock();
12
+ batchingRerenderLocker.lock();
13
13
  element.props['onChange'](event);
14
- syncRerenderLocker.flush();
14
+ batchingRerenderLocker.flush();
15
15
  element = getElementFromDom(event.target);
16
16
  }
17
17
  if (element) {
@@ -24,9 +24,9 @@ function onControlledTextareaInput(event) {
24
24
  return;
25
25
  }
26
26
  if (element.props['onInput']) {
27
- syncRerenderLocker.lock();
27
+ batchingRerenderLocker.lock();
28
28
  element.props['onInput'](event);
29
- syncRerenderLocker.flush();
29
+ batchingRerenderLocker.flush();
30
30
  element = getElementFromDom(event.target);
31
31
  }
32
32
  if (element) {
@@ -1,4 +1,4 @@
1
- import type { Maybe } from '../../shared';
1
+ import type { Maybe } from '../../shared/index.js';
2
2
  import type { SimpElement } from '../../core/internal.js';
3
3
  export declare function patchDangerInnerHTML(prevValue: Maybe<{
4
4
  __html: string;
@@ -1 +1 @@
1
- export * from './props';
1
+ export * from './props.js';
@@ -1 +1 @@
1
- export * from './props';
1
+ export * from './props.js';
@@ -1,5 +1,5 @@
1
1
  import type { SimpElement } from '../../core/internal.js';
2
- import type { Namespace } from '../namespace';
2
+ import type { Namespace } from '../namespace.js';
3
3
  export declare function mountProps(dom: HTMLElement | SVGElement, element: SimpElement, namespace: Namespace): void;
4
4
  export declare function unmountProps(dom: HTMLElement | SVGElement, element: SimpElement): void;
5
5
  export declare function patchProps(dom: HTMLElement | SVGElement, prevElement: SimpElement, nextElement: SimpElement, namespace: Namespace): void;
@@ -1,9 +1,9 @@
1
- import { emptyObject } from '../../shared';
2
- import { defaultNamespace } from '../namespace';
3
- import { addControlledFormElementEventHandlers, isEventNameIgnored, isFormElementControlled, removeControlledFormElementEventHandlers, syncControlledFormElementPropsWithAttrs, } from './controlled';
4
- import { isPropNameEventName, patchEvent } from '../events';
5
- import { patchStyle } from './style';
6
- import { patchDangerInnerHTML } from './dangerInnerHTML';
1
+ import { emptyObject } from '../../shared/index.js';
2
+ import { defaultNamespace } from '../namespace.js';
3
+ import { addControlledFormElementEventHandlers, isEventNameIgnored, isFormElementControlled, removeControlledFormElementEventHandlers, syncControlledFormElementPropsWithAttrs, } from './controlled/index.js';
4
+ import { isPropNameEventName, patchEvent } from '../events.js';
5
+ import { patchStyle } from './style.js';
6
+ import { patchDangerInnerHTML } from './dangerInnerHTML.js';
7
7
  export function mountProps(dom, element, namespace) {
8
8
  if (!isFormElement(element)) {
9
9
  for (const propName in element.props) {
@@ -14,19 +14,22 @@ export function patchStyle(prevAttrValue, nextAttrValue, dom) {
14
14
  for (style in nextAttrValue) {
15
15
  value = nextAttrValue[style];
16
16
  if (value !== prevAttrValue[style]) {
17
- domStyle.setProperty(style, value);
17
+ domStyle.setProperty(camelToKebab(style), value);
18
18
  }
19
19
  }
20
20
  for (style in prevAttrValue) {
21
21
  if (nextAttrValue[style] == null) {
22
- domStyle.removeProperty(style);
22
+ domStyle.removeProperty(camelToKebab(style));
23
23
  }
24
24
  }
25
25
  }
26
26
  else {
27
27
  for (style in nextAttrValue) {
28
28
  value = nextAttrValue[style];
29
- domStyle.setProperty(style, value);
29
+ domStyle.setProperty(camelToKebab(style), value);
30
30
  }
31
31
  }
32
32
  }
33
+ function camelToKebab(name) {
34
+ return name.replace(/[A-Z]/g, match => '-' + match.toLowerCase());
35
+ }
package/dom/render.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type SimpElement } from '../core/internal.js';
2
- import type { Nullable } from '../shared';
2
+ import type { Nullable } from '../shared/index.js';
3
3
  export declare function render(element: Nullable<SimpElement>, container: Nullable<Element | DocumentFragment>): void;
4
4
  interface SimpRoot {
5
5
  render(element: SimpElement): void;
package/dom/render.js CHANGED
@@ -1,13 +1,13 @@
1
- import { asyncRerenderLocker, hostAdapter, mount, patch, provideHostAdapter, remove, } from '../core/internal.js';
2
- import { domAdapter } from './domAdapter';
3
- import { attachElementToDom, getElementFromDom } from './attach-element-to-dom';
1
+ import { renderingRerenderLocker, hostAdapter, mount, patch, provideHostAdapter, remove, } from '../core/internal.js';
2
+ import { domAdapter } from './domAdapter.js';
3
+ import { attachElementToDom, getElementFromDom } from './attach-element-to-dom.js';
4
4
  provideHostAdapter(domAdapter);
5
5
  export function render(element, container) {
6
6
  if (!container) {
7
7
  return;
8
8
  }
9
9
  const currentRootElement = getElementFromDom(container);
10
- asyncRerenderLocker.lock();
10
+ renderingRerenderLocker.lock();
11
11
  if (!currentRootElement) {
12
12
  if (element) {
13
13
  hostAdapter.clearNode(container);
@@ -27,7 +27,7 @@ export function render(element, container) {
27
27
  patch(prevChildren, element, container, null, null, hostAdapter.getHostNamespaces(element, undefined)?.self);
28
28
  }
29
29
  }
30
- asyncRerenderLocker.flush();
30
+ renderingRerenderLocker.flush();
31
31
  }
32
32
  export function createRoot(container) {
33
33
  return {
package/hooks/index.d.ts CHANGED
@@ -1,15 +1,21 @@
1
- import type { RefObject, SimpContext } from '../core';
1
+ import type { RefObject, SimpContext } from '../core/index.js';
2
2
 
3
3
  export type Cleanup = () => void;
4
4
  export type Effect = () => void | Cleanup;
5
5
  export type DependencyList = readonly unknown[];
6
6
 
7
+ export type Dispatch<A> = (value: A) => void;
8
+ export type SetStateAction<S> = S | ((prevState: S) => S);
9
+
7
10
  declare function useRef<T>(initialValue: T): RefObject<T>;
8
11
  declare function useRef<T>(initialValue: T | null): RefObject<T | null>;
9
12
  declare function useRef<T>(initialValue: T | undefined): RefObject<T | undefined>;
10
13
 
11
14
  declare function useRerender(): () => void;
12
15
 
16
+ export function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
17
+ export function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
18
+
13
19
  declare function useEffect(effect: Effect, deps?: DependencyList): void;
14
20
 
15
21
  declare function useMounted(effect: Effect): void;
package/hooks/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { lifecycleEventBus, rerender, syncRerenderLocker } from '../core/internal.js';
2
- import { noop } from '../shared';
1
+ import { batchingRerenderLocker, lifecycleEventBus, rerender as _rerender } from '../core/internal.js';
2
+ import { noop } from '../shared/index.js';
3
+ import { callOrGet } from '../shared/utils.js';
3
4
  let currentIndex = 0;
4
5
  // In runtime this is a nullable variable.
5
6
  let currentElement;
@@ -9,6 +10,9 @@ lifecycleEventBus.subscribe(event => {
9
10
  if (currentElement.store?.catchHandlers) {
10
11
  currentElement.store.catchHandlers = undefined;
11
12
  }
13
+ if (currentElement.store?.effectsHookStates) {
14
+ currentElement.store.effectsHookStates = undefined;
15
+ }
12
16
  }
13
17
  if (event.type === 'afterRender' || event.type === 'errored') {
14
18
  currentElement = null;
@@ -17,19 +21,19 @@ lifecycleEventBus.subscribe(event => {
17
21
  if (event.type === 'mounted') {
18
22
  const element = event.element;
19
23
  if (element.store?.effectsHookStates) {
20
- syncRerenderLocker.lock();
24
+ batchingRerenderLocker.lock();
21
25
  const effects = element.store.effectsHookStates;
22
26
  element.store.effectsHookStates = undefined;
23
27
  for (const state of effects) {
24
28
  state.cleanup = state.effect() || undefined;
25
29
  }
26
- syncRerenderLocker.flush();
30
+ batchingRerenderLocker.flush();
27
31
  }
28
32
  }
29
33
  if (event.type === 'updated') {
30
34
  const element = event.element;
31
35
  if (element.store?.effectsHookStates) {
32
- syncRerenderLocker.lock();
36
+ batchingRerenderLocker.lock();
33
37
  const effects = element.store.effectsHookStates;
34
38
  element.store.effectsHookStates = undefined;
35
39
  for (const state of effects) {
@@ -38,7 +42,7 @@ lifecycleEventBus.subscribe(event => {
38
42
  }
39
43
  state.cleanup = state.effect() || undefined;
40
44
  }
41
- syncRerenderLocker.flush();
45
+ batchingRerenderLocker.flush();
42
46
  }
43
47
  }
44
48
  if (event.type === 'unmounted') {
@@ -57,11 +61,11 @@ lifecycleEventBus.subscribe(event => {
57
61
  throw new Error('Error occurred during rendering a component', { cause: event.error });
58
62
  }
59
63
  if (element.store.catchHandlers) {
60
- syncRerenderLocker.lock();
64
+ batchingRerenderLocker.lock();
61
65
  for (const state of element.store.catchHandlers) {
62
66
  state(event.error);
63
67
  }
64
- syncRerenderLocker.flush();
68
+ batchingRerenderLocker.flush();
65
69
  }
66
70
  }
67
71
  });
@@ -86,7 +90,26 @@ export function useRerender() {
86
90
  const hookStates = getOrCreateHookStates(currentElement);
87
91
  if (!hookStates[currentIndex]) {
88
92
  const elementStore = currentElement.store;
89
- hookStates[currentIndex] = () => rerender(elementStore.latestElement);
93
+ hookStates[currentIndex] = function rerender() {
94
+ _rerender(elementStore.latestElement);
95
+ };
96
+ }
97
+ return hookStates[currentIndex++];
98
+ }
99
+ export function useState(initialState) {
100
+ const hookStates = getOrCreateHookStates(currentElement);
101
+ if (!hookStates[currentIndex]) {
102
+ const elementStore = currentElement.store;
103
+ const state = (hookStates[currentIndex] = [undefined, undefined]);
104
+ state[0] = callOrGet(initialState);
105
+ state[1] = function dispatch(action) {
106
+ const nextValue = callOrGet(action, state[0]);
107
+ if (Object.is(state[0], nextValue)) {
108
+ return;
109
+ }
110
+ state[0] = nextValue;
111
+ _rerender(elementStore.latestElement);
112
+ };
90
113
  }
91
114
  return hookStates[currentIndex++];
92
115
  }
@@ -159,4 +182,14 @@ function getOrCreateEffectHookStates(element) {
159
182
  }
160
183
  return element.store.effectsHookStates;
161
184
  }
162
- export default { useRef, useRerender, useEffect, useMounted, useUnmounted, useContext, useCatch, areDepsEqual };
185
+ export default {
186
+ useRef,
187
+ useRerender,
188
+ useState,
189
+ useEffect,
190
+ useMounted,
191
+ useUnmounted,
192
+ useContext,
193
+ useCatch,
194
+ areDepsEqual,
195
+ };
@@ -1,5 +1,5 @@
1
- import type { FC, Fragment as FragmentType, Key, SimpElement } from '../core';
2
- import type { Maybe } from '../shared';
1
+ import type { FC, Fragment as FragmentType, Key, SimpElement } from '../core/index.js';
2
+ import type { Maybe } from '../shared/index.js';
3
3
  import type {
4
4
  AnchorHTMLAttributes,
5
5
  AreaHTMLAttributes,
@@ -54,7 +54,7 @@ import type {
54
54
  TrackHTMLAttributes,
55
55
  VideoHTMLAttributes,
56
56
  WebViewHTMLAttributes,
57
- } from '../dom';
57
+ } from '../dom/index.js';
58
58
 
59
59
  declare function jsx<P = {}>(type: string | FC<P>, props?: P, key?: Maybe<Key>): SimpElement<P>;
60
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simpreact/simpreact",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/dPaskhin/simpreact#readme",
6
6
  "main": "./core/index.js",
@@ -11,6 +11,12 @@
11
11
  "import": "./core/index.js",
12
12
  "types": "./core/index.d.ts"
13
13
  },
14
+ "./compat": {
15
+ "import": "./compat/index.js"
16
+ },
17
+ "./compat/*": {
18
+ "import": "./compat/index.js"
19
+ },
14
20
  "./internal": {
15
21
  "import": "./core/internal.js",
16
22
  "types": "./core/internal.d.ts"
@@ -44,6 +50,7 @@
44
50
  "url": "git+https://github.com/dPaskhin/simpreact.git"
45
51
  },
46
52
  "sideEffects": false,
53
+ "type": "module",
47
54
  "license": "MIT",
48
55
  "author": "Dmitrii Paskhin <d.pasxin@gmail.com>",
49
56
  "dependencies": {
package/shared/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { EventBus } from './EventBus';
2
- import { emptyArray, emptyMap, emptyObject } from './lang';
3
- import { isSimpText, noop } from './utils';
1
+ import { EventBus } from './EventBus.js';
2
+ import { emptyArray, emptyMap, emptyObject } from './lang.js';
3
+ import { isSimpText, noop } from './utils.js';
4
4
  export { emptyObject, emptyMap, emptyArray, isSimpText, EventBus, noop };
5
5
  export default { isSimpText, EMPTY_MAP: emptyMap, EMPTY_ARRAY: emptyArray, EMPTY_OBJECT: emptyObject, EventBus, noop };
package/shared/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import type { SimpText } from './public';
1
+ import type { SimpText } from './public.js';
2
2
  export declare function isSimpText(value: unknown): value is SimpText;
3
3
  export declare function noop(): void;
4
+ export declare function callOrGet<T, A extends any[]>(value: T | ((...args: A) => T), ...args: A): T;
package/shared/utils.js CHANGED
@@ -2,3 +2,19 @@ export function isSimpText(value) {
2
2
  return typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint';
3
3
  }
4
4
  export function noop() { }
5
+ export function callOrGet(value) {
6
+ if (typeof value !== 'function') {
7
+ return value;
8
+ }
9
+ if (arguments.length === 1) {
10
+ return value();
11
+ }
12
+ if (arguments.length === 2) {
13
+ return value(arguments[1]);
14
+ }
15
+ const args = [];
16
+ for (let i = 1; i < arguments.length; ++i) {
17
+ args.push(arguments[i]);
18
+ }
19
+ return value(...args);
20
+ }