@e280/sly 0.1.0 → 0.2.0-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
@@ -18,7 +18,7 @@
18
18
  ## 🦝 sly and friends
19
19
 
20
20
  ```sh
21
- npm install @e280/sly lit @e280/strata @e280/stz
21
+ npm install @e280/sly lit
22
22
  ```
23
23
 
24
24
  > [!NOTE]
@@ -77,33 +77,34 @@ view(use => () => html`<p>hello world</p>`)
77
77
  // <my-counter></my-counter>
78
78
  ```
79
79
 
80
+ ### 🍋 view declaration settings
81
+ - special settings for views at declaration-time
82
+ ```ts
83
+ export const CoolView = view
84
+ .settings({mode: "open", delegatesFocus: true})
85
+ .declare(use => (greeting: string) => {
86
+ return html`😎 ${greeting} <slot></slot>`
87
+ })
88
+ ```
89
+ - all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
90
+ - note the `<slot></slot>` we'll use in the next example lol
91
+
80
92
  ### 🍋 view injection options
81
93
  - options for views at the template injection site
82
94
  ```ts
83
95
  $.render($(".app"), html`
84
96
  <h2>super cool example</h2>
85
- ${CoolView
97
+
98
+ ${CoolView.props("hello")
86
99
  .attr("class", "hero")
87
100
  .children(html`<em>spongebob</em>`)
88
- .props("hello")}
101
+ .render()}
89
102
  `)
90
103
  ```
104
+ - `props` — provide props and start a view chain
91
105
  - `attr` — set html attributes on the `<sly-view>` host element
92
106
  - `children` — nested content in the host element, can be [slotted](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
93
- - `props` — finally inject the view by providing its props
94
-
95
- ### 🍋 view declaration settings
96
- - special settings for views at declaration-time
97
- ```ts
98
- export const CoolView = view
99
- .settings({mode: "open", delegatesFocus: true})
100
- .view(use => (greeting: string) => {
101
-
102
- return html`😎 ${greeting} <slot></slot>`
103
- })
104
- ```
105
- - all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
106
- - note the `<slot></slot>` we'll use in the next example lol
107
+ - `render` — end the view chain and render the lit directive
107
108
 
108
109
  ### 🍋 web components
109
110
  - **build a component directly**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-0",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,12 +13,14 @@
13
13
  "s"
14
14
  ],
15
15
  "peerDependencies": {
16
- "@e280/strata": "^0.1.0",
17
- "@e280/stz": "^0.1.0",
18
16
  "lit": "^3.3.1"
19
17
  },
18
+ "dependencies": {
19
+ "@e280/strata": "^0.2.0-0",
20
+ "@e280/stz": "^0.2.0"
21
+ },
20
22
  "devDependencies": {
21
- "@e280/science": "^0.1.0",
23
+ "@e280/science": "^0.1.1",
22
24
  "@e280/scute": "^0.0.0",
23
25
  "http-server": "^14.1.1",
24
26
  "npm-run-all": "^4.1.5",
@@ -14,19 +14,19 @@ export const CounterView = view(use => (initial: number) => {
14
14
 
15
15
  use.mount(() => repeat(async() => {
16
16
  const since = Date.now() - start
17
- seconds(Math.floor(since / 1000))
17
+ seconds.set(Math.floor(since / 1000))
18
18
  }))
19
19
 
20
20
  const count = use.signal(initial)
21
- const increment = () => count(count() + 1)
21
+ const increment = () => count.value++
22
22
 
23
23
  return html`
24
24
  <slot></slot>
25
25
  <div>
26
- <span>${seconds()}</span>
26
+ <span>${seconds.get()}</span>
27
27
  </div>
28
28
  <div>
29
- <span>${count()}</span>
29
+ <span>${count.get()}</span>
30
30
  <button @click="${increment}">+</button>
31
31
  </div>
32
32
  `
@@ -10,7 +10,7 @@ export const DemoView = view(use => () => {
10
10
  use.styles(cssReset, styles)
11
11
 
12
12
  return html`
13
- ${CounterView.children("view")(2)}
13
+ ${CounterView.props(2).children("view").render()}
14
14
  ${LoadersView()}
15
15
  `
16
16
  })
@@ -21,11 +21,11 @@ export const AsciiAnim = view(use => ({hz, frames}: {
21
21
 
22
22
  use.mount(() => repeat(async() => {
23
23
  await nap(1000 / hz)
24
- const next = frame() + 1
25
- frame(next >= frames.length ? 0 : next)
24
+ const next = frame.get() + 1
25
+ frame.set(next >= frames.length ? 0 : next)
26
26
  }))
27
27
 
28
- return frames.at(frame())
28
+ return frames.at(frame.get())
29
29
  })
30
30
 
31
31
  const style = css`
package/s/ops/op.ts CHANGED
@@ -46,16 +46,16 @@ export class Op<V> {
46
46
  get finally() { return this.wait.finally.bind(this.wait) }
47
47
 
48
48
  async setLoading() {
49
- await this.signal(["loading"])
49
+ await this.signal.set(["loading"])
50
50
  }
51
51
 
52
52
  async setReady(value: V) {
53
- await this.signal(["ready", value])
53
+ await this.signal.set(["ready", value])
54
54
  await this.#resolve(value)
55
55
  }
56
56
 
57
57
  async setError(error: any) {
58
- await this.signal(["error", error])
58
+ await this.signal.set(["error", error])
59
59
  await this.#reject(error)
60
60
  }
61
61
 
@@ -79,23 +79,23 @@ export class Op<V> {
79
79
  }
80
80
 
81
81
  get pod() {
82
- return this.signal()
82
+ return this.signal.get()
83
83
  }
84
84
 
85
85
  set pod(p: Pod<V>) {
86
- this.signal(p)
86
+ this.signal.set(p)
87
87
  }
88
88
 
89
89
  get status() {
90
- return this.signal()[0]
90
+ return this.signal.get()[0]
91
91
  }
92
92
 
93
93
  get value() {
94
- return podium.value(this.signal())
94
+ return podium.value(this.signal.get())
95
95
  }
96
96
 
97
97
  get error() {
98
- return podium.error(this.signal())
98
+ return podium.error(this.signal.get())
99
99
  }
100
100
 
101
101
  get isLoading() {
@@ -111,13 +111,13 @@ export class Op<V> {
111
111
  }
112
112
 
113
113
  require() {
114
- const pod = this.signal()
114
+ const pod = this.signal.get()
115
115
  if (pod[0] !== "ready") throw new Error("required value not ready")
116
116
  return pod[1]
117
117
  }
118
118
 
119
119
  select<R>(select: PodSelect<V, R>) {
120
- return podium.select(this.signal(), select)
120
+ return podium.select(this.signal.get(), select)
121
121
  }
122
122
 
123
123
  morph<V2>(fn: (value: V) => V2) {
package/s/views/types.ts CHANGED
@@ -16,11 +16,7 @@ export type ComponentFn = (use: Use) => Content
16
16
  export type ViewFn<Props extends any[]> = (use: Use) => (...props: Props) => Content
17
17
  export type BasicView<Props extends any[]> = (...props: Props) => DirectiveResult<any>
18
18
  export type View<Props extends any[]> = BasicView<Props> & {
19
- props: View<Props>
20
- with: (w: Partial<ViewWith>) => View<Props>
21
- children: (...children: Content[]) => View<Props>
22
- attrs: (attrs: Record<string, AttrValue>) => View<Props>
23
- attr: (name: string, value: AttrValue) => View<Props>
19
+ props: (...props: Props) => ViewChain
24
20
  component: (...props: Props) => Constructor<Component>
25
21
  }
26
22
 
@@ -30,8 +26,15 @@ export type ViewSettings = ShadowRootInit & {
30
26
  styles?: CSSResultGroup
31
27
  }
32
28
 
33
- export type ViewWith = {
34
- children: Content
29
+ export type ViewChain = {
30
+ children(...c: Content[]): ViewChain
31
+ attrs(a: Record<string, AttrValue>): ViewChain
32
+ attr(n: string, v: AttrValue): ViewChain
33
+ render(): DirectiveResult<any>
34
+ }
35
+
36
+ export type ViewContext = {
37
+ children: Content[]
35
38
  attrs: Record<string, AttrValue>
36
39
  }
37
40
 
package/s/views/view.ts CHANGED
@@ -1,22 +1,22 @@
1
1
 
2
2
  import {render} from "lit"
3
3
  import {debounce, MapG} from "@e280/stz"
4
+ import {directive} from "lit/directive.js"
4
5
  import {tracker} from "@e280/strata/tracker"
5
6
  import {AsyncDirective} from "lit/async-directive.js"
6
- import {directive, DirectiveResult} from "lit/directive.js"
7
7
 
8
8
  import {register} from "../dom/register.js"
9
9
  import {applyAttrs} from "./utils/apply-attrs.js"
10
10
  import {applyStyles} from "./utils/apply-styles.js"
11
11
  import {Use, _wrap, _disconnect, _reconnect} from "./use.js"
12
- import {AttrValue, ComponentFn, Content, View, ViewFn, ViewSettings, ViewWith} from "./types.js"
12
+ import {AttrValue, ComponentFn, Content, View, ViewFn, ViewSettings, ViewContext} from "./types.js"
13
13
 
14
14
  export const view = setupView({mode: "open"})
15
15
  export class SlyView extends HTMLElement {}
16
16
  register({SlyView}, {soft: true, upgrade: true})
17
17
 
18
18
  function setupView(settings: ViewSettings) {
19
- function view<Props extends any[]>(fn: ViewFn<Props>) {
19
+ function view<Props extends any[]>(fn: ViewFn<Props>): View<Props> {
20
20
  type Situation = {
21
21
  getElement: () => HTMLElement
22
22
  isComponent: boolean
@@ -27,7 +27,7 @@ function setupView(settings: ViewSettings) {
27
27
  #shadow = this.#element.attachShadow(settings)
28
28
  #renderDebounced = debounce(0, () => this.#renderNow())
29
29
  #tracking = new MapG<any, () => void>
30
- #params!: {with: ViewWith, props: Props}
30
+ #params!: {context: ViewContext, props: Props}
31
31
 
32
32
  #use = new Use(
33
33
  this.#element,
@@ -46,7 +46,7 @@ function setupView(settings: ViewSettings) {
46
46
  #renderNow() {
47
47
  if (!this.#params) return
48
48
  if (!this.isConnected) return
49
- const {with: w, props} = this.#params
49
+ const {context: w, props} = this.#params
50
50
 
51
51
  this.#use[_wrap](() => {
52
52
  // apply html attributes
@@ -71,8 +71,8 @@ function setupView(settings: ViewSettings) {
71
71
  })
72
72
  }
73
73
 
74
- render(w: ViewWith, props: Props) {
75
- this.#params = {with: w, props}
74
+ render(context: ViewContext, props: Props) {
75
+ this.#params = {context: context, props}
76
76
  this.#renderNow()
77
77
  return situation.isComponent ? null : this.#element
78
78
  }
@@ -94,40 +94,55 @@ function setupView(settings: ViewSettings) {
94
94
  isComponent: false,
95
95
  }))
96
96
 
97
- function setupDirective(w: ViewWith): View<Props> {
98
- const rend = (...props: Props): DirectiveResult<any> => d(w, props)
99
- rend.props = rend
100
- rend.with = (w2: Partial<ViewWith>) => setupDirective({...w, ...w2})
101
- rend.children = (...children: Content[]) => setupDirective({...w, children})
102
- rend.attrs = (attrs: Record<string, AttrValue>) => setupDirective({...w, attrs})
103
- rend.attr = (name: string, value: AttrValue) => setupDirective({
104
- ...w,
105
- attrs: {...w.attrs, [name]: value},
106
- })
107
- rend.component = (...props: Props) => class extends HTMLElement {
108
- #directive = directive(make({
97
+ const freshViewContext = (): ViewContext => ({attrs: {}, children: []})
98
+
99
+ function rendy(...props: Props) {
100
+ return d(freshViewContext(), props)
101
+ }
102
+
103
+ rendy.props = (...props: Props) => {
104
+ let ctx = freshViewContext()
105
+ const chain = {
106
+ children(...children: Content[]) {
107
+ ctx = {...ctx, children: [...ctx.children, ...children]}
108
+ return chain
109
+ },
110
+ attrs(attrs: Record<string, AttrValue>) {
111
+ ctx = {...ctx, attrs: {...ctx.attrs, ...attrs}}
112
+ return chain
113
+ },
114
+ attr(name: string, value: AttrValue) {
115
+ ctx = {...ctx, attrs: {...ctx.attrs, [name]: value}}
116
+ return chain
117
+ },
118
+ render() {
119
+ return d(ctx, props)
120
+ },
121
+ }
122
+ return chain
123
+ }
124
+
125
+ rendy.component = (...props: Props) => class extends HTMLElement {
126
+ #directive = directive(
127
+ make({
109
128
  getElement: () => this,
110
129
  isComponent: true,
111
- }))
112
- constructor() {
113
- super()
114
- this.render(...props)
115
- }
116
- render(...props: Props) {
117
- if (this.isConnected)
118
- render(this.#directive(w, props), this)
119
- }
130
+ })
131
+ )
132
+ constructor() {
133
+ super()
134
+ this.render(...props)
135
+ }
136
+ render(...props: Props) {
137
+ if (this.isConnected)
138
+ render(this.#directive(freshViewContext(), props), this)
120
139
  }
121
- return rend
122
140
  }
123
141
 
124
- return setupDirective({
125
- attrs: {},
126
- children: null,
127
- })
142
+ return rendy
128
143
  }
129
144
 
130
- view.view = view
145
+ view.declare = view
131
146
  view.settings = (settings2: Partial<ViewSettings>) => setupView({...settings, ...settings2})
132
147
  view.component = (fn: ComponentFn) => view(use => () => fn(use)).component()
133
148
  return view