diff --git a/examples/src/main.js b/examples/src/main.js
index 68b044a..462f32e 100644
--- a/examples/src/main.js
+++ b/examples/src/main.js
@@ -1,7 +1,13 @@
import {Component, ProviderComponent, Dispatcher, html} from '../../main.js';
import styles from './style.css';
-class MainDispatcher extends Dispatcher {
+class MainComponent extends ProviderComponent {
+ static get styles() {
+ return [styles];
+ }
+ get providerId() {
+ return 'main';
+ }
init() {
return {
select: 0,
@@ -9,7 +15,7 @@ class MainDispatcher extends Dispatcher {
name: 'label'
}
}
- async select(state, i) {
+ async select({state}, i) {
return new Promise((resolve, reject) => {
setTimeout(_ => {
state.select = i;
@@ -18,16 +24,7 @@ class MainDispatcher extends Dispatcher {
}, i*1000);
});
}
-}
-
-class MainComponent extends ProviderComponent {
- static get dispatcher() {
- return MainDispatcher;
- }
- static get styles() {
- return [styles];
- }
- render(state, dispatch){
+ render({state, dispatch}){
return html`
@@ -46,22 +43,16 @@ class MainComponent extends ProviderComponent {
}
}
-class SubDispatcher extends Dispatcher {
+class SubComponent extends Component {
init() {
return {
status: 0
};
}
-}
-
-class SubComponent extends Component {
- static get dispatcher() {
- return SubDispatcher;
- }
- render(state) {
+ render({state, $ctx}) {
return html`
- ${state.$context.sounds[this.dataset.id]}
+ ${$ctx('main').state.sounds[this.dataset.id]}
`;
}
}
diff --git a/main.js b/main.js
index 7f88977..a0f66c5 100644
--- a/main.js
+++ b/main.js
@@ -28,105 +28,70 @@ export function html(strings, ...values){
return new RawHTMLTagFuncOutput(strings, values)
}
-export class Dispatcher extends Function {
- constructor(element, store) {
- super();
- this.element = element;
- this.store = store;
- return new Proxy(this, {
- apply(target, thisArg, args) {
- return target.dispatch(...args);
- }
- });
- }
- async dispatch(action, ...args) {
- this.store.update(await this[action](this.store.proxy(), ...args));
- }
- init(){
- return {}
- }
- updatecontext(state){
- return {...state}
- }
- prop(key, default_value){
- return this.element[key] ? this.element[key] : default_value;
- }
- attr(key, default_value){
- return this.element.hasAttribute(key) ? this.element.getAttribute(key) : default_value;
- }
- proxy() {
- return new Proxy(this, {
- get: (target, prop, receiver) => {
- if(prop == '$context'){
- return this.element.context ? this.element.context.dispatcher.proxy() : null;
- }
- return Reflect.get(target, prop, receiver);
- }
- });
- }
-}
-
export class Store{
constructor(element, state = {}) {
- this.context = null;
this.state = state;
this.element = element;
}
- setContext(store) {
- this.context = store;
- }
update(state) {
this.state = state;
this.element.update();
}
- proxy() {
- return new Proxy(this.state, {
- get: (target, prop, receiver) => {
- if(prop == '$context'){
- return this.element.context ? this.element.context.store.proxy() : null;
- }
- return Reflect.get(target, prop, receiver);
- }
- });
- }
}
export class Component extends HTMLElement{
- static get dispatcher() { return Dispatcher; }
constructor() {
super();
this.store = new Store(this);
- this.dispatcher = new this.constructor.dispatcher(this, this.store);
+ this.context = {};
this.attachShadow({mode: 'open'});
}
static get styles() {
return [];
}
async connectedCallback(){
- await this.dispatcher('init');
+ await this.dispatch('init');
if(this.afterFirstUpdate) this.afterFirstUpdate();
}
+ updatecontext(state){
+ return {...state}
+ }
+ $ctx(contextId) {
+ return this.context[contextId];
+ }
setContext(template) {
const walker = document.createTreeWalker(template);
while(walker.nextNode()){
- walker.currentNode.context = this.context;
- if(walker.currentNode.childNodes.length > 0){
- this.setContext(walker.currentNode);
- }
if(walker.currentNode instanceof Component) {
+ for(const k of Object.keys(this.context)){
+ walker.currentNode.context[k] = this.context[k];
+ }
walker.currentNode.setContext(walker.currentNode.shadowRoot);
}
+ if(walker.currentNode.childNodes.length > 0){
+ this.setContext(walker.currentNode);
+ }
}
}
- get $contextStore() {
- return this.context.store.proxy();
+ get state() {
+ return this.store.state;
}
- get $contextDispatch() {
- return this.context.dispatch.proxy();
+ async dispatch(action, ...args) {
+ this.store.update(await this[action]({
+ ...this,
+ state: this.state,
+ $ctx: contextId => this.context[contextId],
+ dispatch: (..._args) => Reflect.apply(this.dispatch, this, args)
+ }, ...args));
}
render(){ return html``; }
async update(){
- const {strings, values} = this.render(this.store.proxy(), this.dispatcher.proxy());
+ const {strings, values} = this.render({
+ ...this,
+ state: this.state,
+ $ctx: contextId => this.context[contextId],
+ dispatch: (...args) => Reflect.apply(this.dispatch, this, args)
+ })
if(this.template == null) {
this.template = new Template(strings);
this.shadowRoot.appendChild(this.template.fragment);
@@ -150,16 +115,24 @@ export class Component extends HTMLElement{
}
export class ProviderComponent extends Component {
+ get providerId() {
+ return this.hasAttribute('provider-id')
+ ? this.getAttribute('provider-id')
+ : undefined;
+ }
+ set providerId(value) {
+ this.setAttribute('provider-id', value);
+ }
setContext(template) {
const walker = document.createTreeWalker(template);
while(walker.nextNode()){
- walker.currentNode.context = this;
- if(walker.currentNode.childNodes.length > 0){
- this.setContext(walker.currentNode);
- }
if(walker.currentNode instanceof Component) {
+ walker.currentNode.context[this.providerId] = this;
walker.currentNode.setContext(walker.currentNode.shadowRoot);
}
+ if(walker.currentNode.childNodes.length > 0){
+ this.setContext(walker.currentNode);
+ }
}
}
}
@@ -286,7 +259,7 @@ class ValueArray {
const walker = document.createTreeWalker(tmp.fragment);
while(walker.nextNode()) {
if(walker.currentNode instanceof Component) {
- walker.currentNode.dispatcher('updatecontext');
+ walker.currentNode.dispatch('updatecontext');
}
}
}
diff --git a/omusubi.d.ts b/omusubi.d.ts
index 23ec660..5a41610 100644
--- a/omusubi.d.ts
+++ b/omusubi.d.ts
@@ -9,47 +9,36 @@ declare module 'omusubi-js' {
export function defineComponent(tagname: string, options?: ElementDefinitionOptions): (wrapped: typeof Component) => void;
export function html(strings: string[], values: string[]): new () => RawHTMLTagFuncOutput;
-
- export class Dispatcher extends Function {
- constructor(element: unknown, store: unknown);
-
- dispatch(action: string, ...args: string[]): Promise;
-
- init(): void;
-
- prop(key: string, default_value: string): string;
-
- attr(key: string, default_value: string): string;
-
- updatecontext(state: unknown): object;
-
- proxy(): object;
- }
export class Store {
constructor(element: unknown, state?: T);
- setContext(store: unknown): void;
-
update(state: T): void;
-
- proxy(): object;
}
export class Component extends HTMLElement {
- static dispatcher: typeof Dispatcher;
static styles: unknown[];
+ state: object;
+
+ dispatch(action: string, ...args: unknown[]): Promise;
+
connectedCallback(): Promise;
+
+ $ctx(contextId: string): ProviderComponent;
+ updatecontext(state: object): object;
+
setContext(template: Template): void;
- render(state: object, dispatch: unknown): new () => RawHTMLTagFuncOutput;
+ render(props: object): new () => RawHTMLTagFuncOutput;
update(): Promise;
}
export class ProviderComponent extends Component {
+ providerId: string;
+
setContext(template: Template): void;
}