-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtale.go
More file actions
123 lines (106 loc) · 3.25 KB
/
tale.go
File metadata and controls
123 lines (106 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package fairytale
import (
"encoding/json"
"fmt"
"github.com/gosimple/slug"
"github.com/macabot/hypp"
"github.com/macabot/hypp/window"
)
type TaleTarget int
const (
TaleInsideBody TaleTarget = iota
TaleAsBody
TaleAsHTML
)
type TaleSettings struct {
Target TaleTarget
}
var _ Node[struct{}] = &Tale[struct{}]{}
// Control manages the state of a Tale. Typically, a Control manages a single
// property of the state, however a Control can change the whole state.
type Control[S hypp.State] interface {
Render(state S, talePath []int, controlIndex int) *hypp.VNode
UpdateFromEvent(state S, event window.Event) hypp.Dispatchable
UpdateFromMessage(state S, data json.RawMessage) hypp.Dispatchable
}
type TaleEvent[S hypp.State] struct {
Path []int
State S
}
// Tale is a Node that lets you develop and document a component.
type Tale[S hypp.State] struct {
name string
slug string
state S
view func(S) *hypp.VNode
dispatch func(hypp.Dispatchable, hypp.Payload)
controls []Control[S]
settings TaleSettings
stateSubscriber func(S)
}
// New creates a new Tale.
func New[S hypp.State](
name string,
state S,
view func(S) *hypp.VNode,
) *Tale[S] {
var tale *Tale[S]
var dispatch func(dispatchable hypp.Dispatchable, payload hypp.Payload)
dispatch = func(dispatchable hypp.Dispatchable, payload hypp.Payload) {
switch v := dispatchable.(type) {
case hypp.StateAndEffects[S]:
tale.SetState(v.State)
for _, effect := range v.Effects {
effect.Effecter(dispatch, effect.Payload)
}
case hypp.Action[S]:
dispatch(v(tale.state, payload), nil)
case func(S, hypp.Payload) hypp.Dispatchable:
dispatch(v(tale.state, payload), nil)
case hypp.ActionAndPayload[S]:
dispatch(v.Action, v.Payload)
case S:
tale.SetState(v)
default:
panic(fmt.Errorf("fairytale: dispatchable has unexpected type '%[1]T'. Expected type 'StateAndEffects[%[2]T]', 'Action[%[2]T]', 'func(%[2]T, Payload) Dispatchable', 'ActionAndPayload[%[2]T]' or '%[2]T'", dispatchable, tale.state))
}
}
tale = &Tale[S]{
name: name,
slug: slug.Make(name),
state: state,
view: view,
dispatch: dispatch,
}
return tale
}
func (t Tale[S]) Name() string { return t.name }
func (t Tale[S]) Slug() string { return t.slug }
func (t Tale[S]) Children() []Node[S] { return nil }
func (t *Tale[S]) Tale() *Tale[S] { return t }
func (t Tale[S]) IsOpen() bool { return false }
func (t *Tale[S]) SetIsOpen(isOpen bool) { /* noop */ }
func (t Tale[S]) View() *hypp.VNode { return t.view(t.state) }
func (t Tale[S]) Controls() []Control[S] { return t.controls }
func (t Tale[S]) State() S { return t.state }
func (t Tale[S]) Settings() TaleSettings { return t.settings }
func (t *Tale[S]) SetStateSubscriber(stateSubscriber func(S)) {
t.stateSubscriber = stateSubscriber
}
func (t *Tale[S]) Dispatch(dispatchable hypp.Dispatchable, payload hypp.Payload) {
t.dispatch(dispatchable, payload)
}
func (t *Tale[S]) SetState(s S) {
t.state = s
if t.stateSubscriber != nil {
t.stateSubscriber(s)
}
}
func (t *Tale[S]) WithControls(controls ...Control[S]) *Tale[S] {
t.controls = controls
return t
}
func (t *Tale[S]) WithSettings(settings TaleSettings) *Tale[S] {
t.settings = settings
return t
}