@e280/sly 0.2.0-8 → 0.2.0-9

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 (91) hide show
  1. package/README.md +87 -24
  2. package/package.json +1 -1
  3. package/s/demo/demo.bundle.ts +13 -1
  4. package/s/demo/views/counter.ts +13 -5
  5. package/s/demo/views/demo.ts +10 -0
  6. package/s/demo/views/divine.ts +22 -0
  7. package/s/demo/views/incredi.ts +2 -1
  8. package/s/dom/attrs/attrs.ts +21 -0
  9. package/s/dom/attrs/parts/attr-fns.ts +38 -0
  10. package/s/dom/attrs/parts/attr-proxies.ts +35 -0
  11. package/s/dom/attrs/parts/attr-spec.ts +29 -0
  12. package/s/dom/attrs/parts/on-attrs.ts +8 -0
  13. package/s/dom/dom.ts +6 -5
  14. package/s/dom/{register.ts → parts/register.ts} +2 -10
  15. package/s/dom/types.ts +45 -0
  16. package/s/index.html.ts +2 -1
  17. package/s/index.ts +1 -4
  18. package/s/views/base-element.ts +15 -31
  19. package/s/views/types.ts +15 -6
  20. package/s/views/use.ts +10 -11
  21. package/s/views/utils/attr-watcher.ts +22 -0
  22. package/s/views/utils/reactor.ts +23 -0
  23. package/s/views/view.ts +73 -38
  24. package/x/demo/demo.bundle.js +11 -1
  25. package/x/demo/demo.bundle.js.map +1 -1
  26. package/x/demo/demo.bundle.min.js +19 -15
  27. package/x/demo/demo.bundle.min.js.map +4 -4
  28. package/x/demo/views/counter.d.ts +7 -1
  29. package/x/demo/views/counter.js +11 -5
  30. package/x/demo/views/counter.js.map +1 -1
  31. package/x/demo/views/demo.js +8 -0
  32. package/x/demo/views/demo.js.map +1 -1
  33. package/x/demo/views/divine.d.ts +8 -0
  34. package/x/demo/views/divine.js +19 -0
  35. package/x/demo/views/divine.js.map +1 -0
  36. package/x/demo/views/incredi.js +1 -1
  37. package/x/demo/views/incredi.js.map +1 -1
  38. package/x/dom/attrs/attrs.d.ts +20 -0
  39. package/x/dom/attrs/attrs.js +17 -0
  40. package/x/dom/attrs/attrs.js.map +1 -0
  41. package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
  42. package/x/dom/attrs/parts/attr-fns.js +42 -0
  43. package/x/dom/attrs/parts/attr-fns.js.map +1 -0
  44. package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
  45. package/x/dom/attrs/parts/attr-proxies.js +21 -0
  46. package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
  47. package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
  48. package/x/dom/attrs/parts/attr-spec.js +21 -0
  49. package/x/dom/attrs/parts/attr-spec.js.map +1 -0
  50. package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
  51. package/x/dom/attrs/parts/on-attrs.js +7 -0
  52. package/x/dom/attrs/parts/on-attrs.js.map +1 -0
  53. package/x/dom/dom.d.ts +10 -4
  54. package/x/dom/dom.js +5 -5
  55. package/x/dom/dom.js.map +1 -1
  56. package/x/dom/parts/dashify.js.map +1 -0
  57. package/x/dom/{register.d.ts → parts/register.d.ts} +2 -10
  58. package/x/dom/parts/register.js.map +1 -0
  59. package/x/dom/{attributes.d.ts → types.d.ts} +11 -2
  60. package/x/dom/types.js +2 -0
  61. package/x/dom/types.js.map +1 -0
  62. package/x/index.d.ts +1 -4
  63. package/x/index.html +4 -3
  64. package/x/index.html.js +2 -1
  65. package/x/index.html.js.map +1 -1
  66. package/x/index.js +1 -4
  67. package/x/index.js.map +1 -1
  68. package/x/views/base-element.js +9 -27
  69. package/x/views/base-element.js.map +1 -1
  70. package/x/views/types.d.ts +11 -6
  71. package/x/views/use.d.ts +2 -2
  72. package/x/views/use.js +2 -5
  73. package/x/views/use.js.map +1 -1
  74. package/x/views/utils/attr-watcher.d.ts +8 -0
  75. package/x/views/utils/attr-watcher.js +20 -0
  76. package/x/views/utils/attr-watcher.js.map +1 -0
  77. package/x/views/utils/reactor.d.ts +6 -0
  78. package/x/views/utils/reactor.js +18 -0
  79. package/x/views/utils/reactor.js.map +1 -0
  80. package/x/views/view.d.ts +8 -4
  81. package/x/views/view.js +59 -30
  82. package/x/views/view.js.map +1 -1
  83. package/s/dom/attributes.ts +0 -89
  84. package/x/dom/attributes.js +0 -46
  85. package/x/dom/attributes.js.map +0 -1
  86. package/x/dom/dashify.js.map +0 -1
  87. package/x/dom/register.js.map +0 -1
  88. /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
  89. /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
  90. /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
  91. /package/x/dom/{register.js → parts/register.js} +0 -0
@@ -1,12 +1,12 @@
1
1
 
2
+ import {debounce} from "@e280/stz"
2
3
  import {CSSResultGroup} from "lit"
3
- import {tracker} from "@e280/strata"
4
- import {debounce, MapG} from "@e280/stz"
5
4
 
6
5
  import {dom} from "../dom/dom.js"
7
6
  import {Content} from "./types.js"
8
- import {onAttrChange} from "../dom/attributes.js"
7
+ import {Reactor} from "./utils/reactor.js"
9
8
  import {applyStyles} from "./utils/apply-styles.js"
9
+ import {AttrWatcher} from "./utils/attr-watcher.js"
10
10
  import {Use, _disconnect, _reconnect, _wrap} from "./use.js"
11
11
 
12
12
  export abstract class BaseElement extends HTMLElement {
@@ -15,7 +15,8 @@ export abstract class BaseElement extends HTMLElement {
15
15
 
16
16
  #use: Use
17
17
  #mounts = 0
18
- #tracking = new MapG<any, () => void>
18
+ #reactor = new Reactor()
19
+ #attrWatcher = new AttrWatcher(this, () => this.update())
19
20
 
20
21
  constructor() {
21
22
  super()
@@ -32,15 +33,13 @@ export abstract class BaseElement extends HTMLElement {
32
33
 
33
34
  updateNow = () => {
34
35
  this.#use[_wrap](() => {
35
- const {result, seen} = tracker.observe(() => this.render(this.#use))
36
-
37
- dom.render(this.shadow, result)
38
-
39
- for (const item of seen)
40
- this.#tracking.guarantee(
41
- item,
42
- () => tracker.subscribe(item, this.update),
43
- )
36
+ dom.render(
37
+ this.shadow,
38
+ this.#reactor.effect(
39
+ () => this.render(this.#use),
40
+ this.update,
41
+ ),
42
+ )
44
43
  })
45
44
  }
46
45
 
@@ -56,29 +55,14 @@ export abstract class BaseElement extends HTMLElement {
56
55
  else {
57
56
  this.#use[_reconnect]()
58
57
  }
59
- this.#attrListening.start()
58
+ this.#attrWatcher.start()
60
59
  this.#mounts++
61
60
  }
62
61
 
63
62
  disconnectedCallback() {
64
- this.#attrListening.stop()
65
63
  this.#use[_disconnect]()
66
- for (const untrack of this.#tracking.values())
67
- untrack()
68
- this.#tracking.clear()
64
+ this.#reactor.clear()
65
+ this.#attrWatcher.stop()
69
66
  }
70
-
71
- #attrListening = (() => {
72
- let stopper: (() => void) | undefined
73
- const start = () => {
74
- if (stopper) stopper()
75
- stopper = onAttrChange(this, () => this.update)
76
- }
77
- const stop = () => {
78
- if (stopper) stopper()
79
- stopper = undefined
80
- }
81
- return {start, stop}
82
- })()
83
67
  }
84
68
 
package/s/views/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
 
2
- import {Constructor} from "@e280/stz"
3
2
  import {DirectiveResult} from "lit/directive.js"
4
3
  import {CSSResultGroup, TemplateResult} from "lit"
5
4
 
@@ -8,16 +7,26 @@ import {Use} from "./use.js"
8
7
  export type Content = TemplateResult | DirectiveResult | HTMLElement | string | null | undefined | void | Content[]
9
8
  export type AttrValue = string | boolean | number | undefined | null | void
10
9
 
11
- export type Component<Props extends any[] = []> = {
12
- render(...props: Props): void
10
+ export type Component<Mix extends {} = {}> = Mix & {
11
+ renderNow(): void
12
+ render: () => void
13
13
  } & HTMLElement
14
14
 
15
- export type ComponentFn = (use: Use) => Content
16
- export type ViewFn<Props extends any[]> = (use: Use) => (...props: Props) => Content
15
+ export type ComponentClass<Mix extends {}, Props extends any[]> = {
16
+ view: View<Props>
17
+ new(): Component<Mix>
18
+ }
19
+
20
+ export type ViewRenderFn<Props extends any[]> = (use: Use) => (...props: Props) => Content
17
21
  export type BasicView<Props extends any[]> = (...props: Props) => DirectiveResult<any>
22
+
18
23
  export type View<Props extends any[]> = BasicView<Props> & {
19
24
  props: (...props: Props) => ViewChain
20
- component: (...props: Props) => Constructor<Component>
25
+ component: <Mix extends {}>(init?: (component: Component<Mix>) => void) => {
26
+ props: (fn: (component: Component<Mix>) => Props) => (
27
+ ComponentClass<Mix, Props>
28
+ )
29
+ }
21
30
  }
22
31
 
23
32
  export type ViewSettings = ShadowRootInit & {
package/s/views/use.ts CHANGED
@@ -5,15 +5,17 @@ import {signal, SignalOptions} from "@e280/strata/signals"
5
5
 
6
6
  import {Op} from "../ops/op.js"
7
7
  import {dom} from "../dom/dom.js"
8
+ import {Attrs} from "../dom/types.js"
8
9
  import {Mounts} from "./utils/mounts.js"
9
10
  import {applyStyles} from "./utils/apply-styles.js"
10
- import {AttrSpec, onAttrChange} from "../dom/attributes.js"
11
11
 
12
12
  export const _wrap = Symbol()
13
13
  export const _disconnect = Symbol()
14
14
  export const _reconnect = Symbol()
15
15
 
16
16
  export class Use {
17
+ attrs: Attrs
18
+
17
19
  #runs = 0
18
20
  #position = 0
19
21
  #values = new MapG<number, any>()
@@ -38,11 +40,13 @@ export class Use {
38
40
  }
39
41
 
40
42
  constructor(
41
- public element: HTMLElement,
42
- public shadow: ShadowRoot,
43
- public renderNow: () => void,
44
- public render: () => Promise<void>,
45
- ) {}
43
+ public element: HTMLElement,
44
+ public shadow: ShadowRoot,
45
+ public renderNow: () => void,
46
+ public render: () => Promise<void>,
47
+ ) {
48
+ this.attrs = dom.attrs(this.element)
49
+ }
46
50
 
47
51
  get renderCount() {
48
52
  return this.#runs
@@ -65,11 +69,6 @@ export class Use {
65
69
  return this.styles(...styles)
66
70
  }
67
71
 
68
- attrs<A extends AttrSpec>(spec: A) {
69
- this.mount(() => onAttrChange(this.element, this.render))
70
- return this.once(() => dom.attrs(this.element, spec))
71
- }
72
-
73
72
  once<V>(fn: () => V) {
74
73
  return this.#values.guarantee(this.#position++, fn) as V
75
74
  }
@@ -0,0 +1,22 @@
1
+
2
+ import {dom} from "../../dom/dom.js"
3
+
4
+ export class AttrWatcher {
5
+ #stopper: (() => void) | undefined
6
+
7
+ constructor(
8
+ private element: HTMLElement,
9
+ private response: () => void,
10
+ ) {}
11
+
12
+ start() {
13
+ if (!this.#stopper)
14
+ this.#stopper = dom.attrs(this.element).on(this.response)
15
+ }
16
+
17
+ stop() {
18
+ if (this.#stopper) this.#stopper()
19
+ this.#stopper = undefined
20
+ }
21
+ }
22
+
@@ -0,0 +1,23 @@
1
+
2
+ import {MapG} from "@e280/stz"
3
+ import {tracker} from "@e280/strata"
4
+
5
+ export class Reactor {
6
+ #map = new MapG<any, () => void>()
7
+
8
+ constructor() {}
9
+
10
+ effect<R>(collect: () => R, respond: () => Promise<void>) {
11
+ const {seen, result} = tracker.observe(collect)
12
+ for (const item of seen)
13
+ this.#map.guarantee(item, () => tracker.subscribe(item, respond))
14
+ return result
15
+ }
16
+
17
+ clear() {
18
+ for (const dispose of this.#map.values())
19
+ dispose()
20
+ this.#map.clear()
21
+ }
22
+ }
23
+
package/s/views/view.ts CHANGED
@@ -1,22 +1,23 @@
1
1
 
2
2
  import {render} from "lit"
3
- import {debounce, MapG} from "@e280/stz"
3
+ import {debounce} from "@e280/stz"
4
4
  import {directive} from "lit/directive.js"
5
- import {tracker} from "@e280/strata/tracker"
6
5
  import {AsyncDirective} from "lit/async-directive.js"
7
6
 
8
- import {register} from "../dom/register.js"
7
+ import {Reactor} from "./utils/reactor.js"
8
+ import {register} from "../dom/parts/register.js"
9
9
  import {applyAttrs} from "./utils/apply-attrs.js"
10
+ import {AttrWatcher} from "./utils/attr-watcher.js"
10
11
  import {applyStyles} from "./utils/apply-styles.js"
11
12
  import {Use, _wrap, _disconnect, _reconnect} from "./use.js"
12
- import {AttrValue, ComponentFn, Content, View, ViewFn, ViewSettings, ViewContext} from "./types.js"
13
+ import {AttrValue, Content, View, ViewRenderFn, ViewSettings, ViewContext, Component, ComponentClass} from "./types.js"
13
14
 
14
15
  export const view = setupView({mode: "open"})
15
16
  export class SlyView extends HTMLElement {}
16
17
  register({SlyView}, {soft: true, upgrade: true})
17
18
 
18
19
  function setupView(settings: ViewSettings) {
19
- function view<Props extends any[]>(fn: ViewFn<Props>): View<Props> {
20
+ function view<Props extends any[]>(fn: ViewRenderFn<Props>): View<Props> {
20
21
  type Situation = {
21
22
  getElement: () => HTMLElement
22
23
  isComponent: boolean
@@ -25,9 +26,16 @@ function setupView(settings: ViewSettings) {
25
26
  const make = (situation: Situation) => class ViewDirective extends AsyncDirective {
26
27
  #element = situation.getElement()
27
28
  #shadow = this.#element.attachShadow(settings)
29
+ #reactor = new Reactor()
28
30
  #renderDebounced = debounce(0, () => this.#renderNow())
29
- #tracking = new MapG<any, () => void>
30
31
  #params!: {context: ViewContext, props: Props}
32
+ #attrWatcher = new AttrWatcher(this.#element, () => {
33
+ const is_view_responsible_for_rerendering = (
34
+ !situation.isComponent
35
+ )
36
+ if (is_view_responsible_for_rerendering)
37
+ this.#renderDebounced()
38
+ })
31
39
 
32
40
  #use = new Use(
33
41
  this.#element,
@@ -52,18 +60,14 @@ function setupView(settings: ViewSettings) {
52
60
  // apply html attributes
53
61
  applyAttrs(this.#element, context.attrs)
54
62
 
55
- // render the template, tracking strata items
56
- const {result, seen} = tracker.observe(() => this.#fn(...props))
63
+ // render the template and track reactivity
64
+ const content = this.#reactor.effect(
65
+ () => this.#fn(...props),
66
+ this.#renderDebounced,
67
+ )
57
68
 
58
69
  // inject the template
59
- render(result, this.#shadow)
60
-
61
- // reacting to changes
62
- for (const item of seen)
63
- this.#tracking.guarantee(
64
- item,
65
- () => tracker.subscribe(item, async() => this.#renderDebounced()),
66
- )
70
+ render(content, this.#shadow)
67
71
 
68
72
  // inject content into light dom
69
73
  if (!situation.isComponent)
@@ -74,18 +78,21 @@ function setupView(settings: ViewSettings) {
74
78
  render(context: ViewContext, props: Props) {
75
79
  this.#params = {context, props}
76
80
  this.#renderNow()
77
- return situation.isComponent ? null : this.#element
81
+ this.#attrWatcher.start()
82
+ return situation.isComponent
83
+ ? null
84
+ : this.#element
78
85
  }
79
86
 
80
87
  disconnected() {
81
88
  this.#use[_disconnect]()
82
- for (const untrack of this.#tracking.values())
83
- untrack()
84
- this.#tracking.clear()
89
+ this.#reactor.clear()
90
+ this.#attrWatcher.stop()
85
91
  }
86
92
 
87
93
  reconnected() {
88
94
  this.#use[_reconnect]()
95
+ this.#attrWatcher.start()
89
96
  }
90
97
  }
91
98
 
@@ -122,29 +129,57 @@ function setupView(settings: ViewSettings) {
122
129
  return chain
123
130
  }
124
131
 
125
- rendy.component = (...props: Props) => class ViewComponent extends HTMLElement {
126
- #context = freshViewContext()
127
- #directive = directive(
128
- make({
129
- getElement: () => this,
130
- isComponent: true,
131
- })
132
- )
133
- connectedCallback() {
134
- this.render(...props)
135
- }
136
- render(...props: Props) {
137
- if (this.isConnected)
138
- render(this.#directive(this.#context, props), this)
139
- }
140
- }
132
+ rendy.component = <Mix extends {} = {}>(
133
+ init: (component: Component<Mix>) => void = () => {}
134
+ ) => ({
135
+ props: (propsFn: (component: Component<Mix>) => Props) => {
136
+ return class VComponent extends HTMLElement implements Component {
137
+ static view = rendy
138
+ #context = freshViewContext()
139
+ #directive = directive(
140
+ make({
141
+ getElement: () => this,
142
+ isComponent: true,
143
+ })
144
+ )
145
+ #attrWatcher = new AttrWatcher(this, () => this.render())
146
+ constructor() {
147
+ super()
148
+ init(this as any)
149
+ }
150
+ connectedCallback() {
151
+ this.#attrWatcher.start()
152
+ this.renderNow()
153
+ }
154
+ disconnectedCallback() {
155
+ this.#attrWatcher.stop()
156
+ this.#reactor.clear()
157
+ }
158
+ #reactor = new Reactor()
159
+ render = debounce(0, () => this.renderNow())
160
+ renderNow() {
161
+ if (this.isConnected) {
162
+ const props = this.#reactor.effect(
163
+ () => propsFn(this as any),
164
+ this.render,
165
+ )
166
+ render(this.#directive(this.#context, props), this)
167
+ }
168
+ }
169
+ } as any as ComponentClass<Mix, Props>
170
+ },
171
+ })
141
172
 
142
173
  return rendy
143
174
  }
144
175
 
145
- view.declare = view
176
+ view.render = view
146
177
  view.settings = (settings2: Partial<ViewSettings>) => setupView({...settings, ...settings2})
147
- view.component = (fn: ComponentFn) => view(use => () => fn(use)).component()
178
+ view.component = <Mix extends {}>(initFn?: (component: Component<Mix>) => void) => ({
179
+ props: <Props extends any[]>(propsFn: (component: Component<Mix>) => Props) => ({
180
+ render: (fn: ViewRenderFn<Props>) => view(fn).component<Mix>(initFn).props(propsFn),
181
+ }),
182
+ })
148
183
  return view
149
184
  }
150
185
 
@@ -1,11 +1,21 @@
1
+ import { nap, repeat } from "@e280/stz";
1
2
  import { dom } from "../dom/dom.js";
2
3
  import { DemoView } from "./views/demo.js";
3
4
  import { CounterView } from "./views/counter.js";
5
+ import { DivineElement } from "./views/divine.js";
4
6
  import { IncrediElement } from "./views/incredi.js";
5
7
  dom.in(".demo").render(DemoView());
6
8
  dom.register({
7
9
  IncrediElement,
8
- DemoCounter: CounterView.component(1),
10
+ DivineElement,
11
+ DemoCounter: CounterView
12
+ .component()
13
+ .props(c => [dom.attrs(c).number.initial ?? 0]),
14
+ });
15
+ const divine = dom("divine-element");
16
+ repeat(async () => {
17
+ await nap(1000);
18
+ divine.$speed.value++;
9
19
  });
10
20
  console.log("🦝 sly");
11
21
  //# sourceMappingURL=demo.bundle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAC,MAAM,eAAe,CAAA;AACjC,OAAO,EAAC,QAAQ,EAAC,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAA;AAEjD,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AAElC,GAAG,CAAC,QAAQ,CAAC;IACZ,cAAc;IACd,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;CACrC,CAAC,CAAA;AAEF,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA"}
1
+ {"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAE,MAAM,EAAC,MAAM,WAAW,CAAA;AACrC,OAAO,EAAC,GAAG,EAAC,MAAM,eAAe,CAAA;AACjC,OAAO,EAAC,QAAQ,EAAC,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAA;AAEjD,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AAElC,GAAG,CAAC,QAAQ,CAAC;IACZ,cAAc;IACd,aAAa;IACb,WAAW,EAAE,WAAW;SACtB,SAAS,EAAE;SACX,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;CAChD,CAAC,CAAA;AAEF,MAAM,MAAM,GAAG,GAAG,CAAgB,gBAAgB,CAAC,CAAA;AAEnD,MAAM,CAAC,KAAK,IAAG,EAAE;IAChB,MAAM,GAAG,CAAC,IAAI,CAAC,CAAA;IACf,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;AACtB,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA"}