Skip to content

Commit

Permalink
Make RenderController are reactive
Browse files Browse the repository at this point in the history
  • Loading branch information
7-zete-7 committed Sep 28, 2024
1 parent 1720a47 commit 4c32054
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 10 deletions.
6 changes: 5 additions & 1 deletion src/Vue/assets/dist/render_controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ export default class extends Controller<Element & {
private props;
private app;
readonly componentValue: string;
readonly propsValue: Record<string, unknown> | null | undefined;
readonly hasPropsValue: boolean;
propsValue: Record<string, unknown> | null | undefined;
static values: {
component: StringConstructor;
props: ObjectConstructor;
};
propsValueChanged(newProps: typeof this.propsValue, oldProps: typeof this.propsValue): void;
initialize(): void;
connect(): void;
disconnect(): void;
private dispatchEvent;
private wrapComponent;
}
45 changes: 42 additions & 3 deletions src/Vue/assets/dist/render_controller.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { Controller } from '@hotwired/stimulus';
import { createApp } from 'vue';
import { shallowReactive, watch, toRaw, createApp, defineComponent, h } from 'vue';

class default_1 extends Controller {
propsValueChanged(newProps, oldProps) {
if (oldProps) {
let removedPropNames = Object.keys(oldProps);
if (newProps) {
removedPropNames = removedPropNames.filter((propName) => !Object.prototype.hasOwnProperty.call(newProps, propName));
}
removedPropNames.forEach((propName) => {
delete this.props[propName];
});
}
if (newProps) {
Object.entries(newProps).forEach(([propName, propValue]) => {
this.props[propName] = propValue;
});
}
}
initialize() {
const props = this.hasPropsValue && this.propsValue ? this.propsValue : {};
this.props = shallowReactive({ ...props });
watch(this.props, (props) => {
this.propsValue = toRaw(props);
}, { flush: 'post' });
}
connect() {
this.props = this.propsValue ?? null;
this.dispatchEvent('connect', { componentName: this.componentValue, props: this.props });
const component = window.resolveVueComponent(this.componentValue);
this.app = createApp(component, this.props);
const wrappedComponent = this.wrapComponent(component);
this.app = createApp(wrappedComponent);
if (this.element.__vue_app__ !== undefined) {
this.element.__vue_app__.unmount();
}
Expand All @@ -33,6 +56,22 @@ class default_1 extends Controller {
dispatchEvent(name, payload) {
this.dispatch(name, { detail: payload, prefix: 'vue' });
}
wrapComponent(component) {
return defineComponent({
setup: () => {
const props = this.props;
return () => h(component, {
...props,
...Object.fromEntries(Object.keys(props).map((propName) => [
`onUpdate:${propName}`,
(value) => {
props[propName] = value;
},
])),
});
},
});
}
}
default_1.values = {
component: String,
Expand Down
76 changes: 70 additions & 6 deletions src/Vue/assets/src/render_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,70 @@
*/

import { Controller } from '@hotwired/stimulus';
import { type App, createApp } from 'vue';
import {
type App,
type Component,
createApp,
defineComponent,
h,
type ShallowReactive,
shallowReactive,
toRaw,
watch,
} from 'vue';

export default class extends Controller<Element & { __vue_app__?: App<Element> }> {
private props: Record<string, unknown> | null;
private props: ShallowReactive<Record<string, unknown>>;
private app: App<Element>;
declare readonly componentValue: string;
declare readonly propsValue: Record<string, unknown> | null | undefined;
declare readonly hasPropsValue: boolean;
declare propsValue: Record<string, unknown> | null | undefined;

static values = {
component: String,
props: Object,
};

connect() {
this.props = this.propsValue ?? null;
propsValueChanged(newProps: typeof this.propsValue, oldProps: typeof this.propsValue) {
if (oldProps) {
let removedPropNames = Object.keys(oldProps);

if (newProps) {
removedPropNames = removedPropNames.filter(
(propName) => !Object.prototype.hasOwnProperty.call(newProps, propName)
);
}

removedPropNames.forEach((propName) => {
delete this.props[propName];
});
}
if (newProps) {
Object.entries(newProps).forEach(([propName, propValue]) => {
this.props[propName] = propValue;
});
}
}

initialize() {
const props = this.hasPropsValue && this.propsValue ? this.propsValue : {};
this.props = shallowReactive({ ...props });
watch(
this.props,
(props) => {
this.propsValue = toRaw(props);
},
{ flush: 'post' }
);
}

connect() {
this.dispatchEvent('connect', { componentName: this.componentValue, props: this.props });

const component = window.resolveVueComponent(this.componentValue);
const wrappedComponent = this.wrapComponent(component);

this.app = createApp(component, this.props);
this.app = createApp(wrappedComponent);

if (this.element.__vue_app__ !== undefined) {
this.element.__vue_app__.unmount();
Expand Down Expand Up @@ -62,4 +105,25 @@ export default class extends Controller<Element & { __vue_app__?: App<Element> }
private dispatchEvent(name: string, payload: any) {
this.dispatch(name, { detail: payload, prefix: 'vue' });
}

private wrapComponent(component: Component): Component {
return defineComponent({
setup: () => {
const props = this.props;

return () =>
h(component, {
...props,
...Object.fromEntries(
Object.keys(props).map((propName) => [
`onUpdate:${propName}`,
(value: unknown) => {
props[propName] = value;
},
])
),
});
},
});
}
}

0 comments on commit 4c32054

Please sign in to comment.