Skip to content

Commit 4c32054

Browse files
committed
Make RenderController are reactive
1 parent 1720a47 commit 4c32054

File tree

3 files changed

+117
-10
lines changed

3 files changed

+117
-10
lines changed

src/Vue/assets/dist/render_controller.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ export default class extends Controller<Element & {
66
private props;
77
private app;
88
readonly componentValue: string;
9-
readonly propsValue: Record<string, unknown> | null | undefined;
9+
readonly hasPropsValue: boolean;
10+
propsValue: Record<string, unknown> | null | undefined;
1011
static values: {
1112
component: StringConstructor;
1213
props: ObjectConstructor;
1314
};
15+
propsValueChanged(newProps: typeof this.propsValue, oldProps: typeof this.propsValue): void;
16+
initialize(): void;
1417
connect(): void;
1518
disconnect(): void;
1619
private dispatchEvent;
20+
private wrapComponent;
1721
}

src/Vue/assets/dist/render_controller.js

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
import { Controller } from '@hotwired/stimulus';
2-
import { createApp } from 'vue';
2+
import { shallowReactive, watch, toRaw, createApp, defineComponent, h } from 'vue';
33

44
class default_1 extends Controller {
5+
propsValueChanged(newProps, oldProps) {
6+
if (oldProps) {
7+
let removedPropNames = Object.keys(oldProps);
8+
if (newProps) {
9+
removedPropNames = removedPropNames.filter((propName) => !Object.prototype.hasOwnProperty.call(newProps, propName));
10+
}
11+
removedPropNames.forEach((propName) => {
12+
delete this.props[propName];
13+
});
14+
}
15+
if (newProps) {
16+
Object.entries(newProps).forEach(([propName, propValue]) => {
17+
this.props[propName] = propValue;
18+
});
19+
}
20+
}
21+
initialize() {
22+
const props = this.hasPropsValue && this.propsValue ? this.propsValue : {};
23+
this.props = shallowReactive({ ...props });
24+
watch(this.props, (props) => {
25+
this.propsValue = toRaw(props);
26+
}, { flush: 'post' });
27+
}
528
connect() {
6-
this.props = this.propsValue ?? null;
729
this.dispatchEvent('connect', { componentName: this.componentValue, props: this.props });
830
const component = window.resolveVueComponent(this.componentValue);
9-
this.app = createApp(component, this.props);
31+
const wrappedComponent = this.wrapComponent(component);
32+
this.app = createApp(wrappedComponent);
1033
if (this.element.__vue_app__ !== undefined) {
1134
this.element.__vue_app__.unmount();
1235
}
@@ -33,6 +56,22 @@ class default_1 extends Controller {
3356
dispatchEvent(name, payload) {
3457
this.dispatch(name, { detail: payload, prefix: 'vue' });
3558
}
59+
wrapComponent(component) {
60+
return defineComponent({
61+
setup: () => {
62+
const props = this.props;
63+
return () => h(component, {
64+
...props,
65+
...Object.fromEntries(Object.keys(props).map((propName) => [
66+
`onUpdate:${propName}`,
67+
(value) => {
68+
props[propName] = value;
69+
},
70+
])),
71+
});
72+
},
73+
});
74+
}
3675
}
3776
default_1.values = {
3877
component: String,

src/Vue/assets/src/render_controller.ts

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,70 @@
88
*/
99

1010
import { Controller } from '@hotwired/stimulus';
11-
import { type App, createApp } from 'vue';
11+
import {
12+
type App,
13+
type Component,
14+
createApp,
15+
defineComponent,
16+
h,
17+
type ShallowReactive,
18+
shallowReactive,
19+
toRaw,
20+
watch,
21+
} from 'vue';
1222

1323
export default class extends Controller<Element & { __vue_app__?: App<Element> }> {
14-
private props: Record<string, unknown> | null;
24+
private props: ShallowReactive<Record<string, unknown>>;
1525
private app: App<Element>;
1626
declare readonly componentValue: string;
17-
declare readonly propsValue: Record<string, unknown> | null | undefined;
27+
declare readonly hasPropsValue: boolean;
28+
declare propsValue: Record<string, unknown> | null | undefined;
1829

1930
static values = {
2031
component: String,
2132
props: Object,
2233
};
2334

24-
connect() {
25-
this.props = this.propsValue ?? null;
35+
propsValueChanged(newProps: typeof this.propsValue, oldProps: typeof this.propsValue) {
36+
if (oldProps) {
37+
let removedPropNames = Object.keys(oldProps);
38+
39+
if (newProps) {
40+
removedPropNames = removedPropNames.filter(
41+
(propName) => !Object.prototype.hasOwnProperty.call(newProps, propName)
42+
);
43+
}
2644

45+
removedPropNames.forEach((propName) => {
46+
delete this.props[propName];
47+
});
48+
}
49+
if (newProps) {
50+
Object.entries(newProps).forEach(([propName, propValue]) => {
51+
this.props[propName] = propValue;
52+
});
53+
}
54+
}
55+
56+
initialize() {
57+
const props = this.hasPropsValue && this.propsValue ? this.propsValue : {};
58+
this.props = shallowReactive({ ...props });
59+
watch(
60+
this.props,
61+
(props) => {
62+
this.propsValue = toRaw(props);
63+
},
64+
{ flush: 'post' }
65+
);
66+
}
67+
68+
connect() {
2769
this.dispatchEvent('connect', { componentName: this.componentValue, props: this.props });
2870

2971
const component = window.resolveVueComponent(this.componentValue);
72+
const wrappedComponent = this.wrapComponent(component);
3073

31-
this.app = createApp(component, this.props);
74+
this.app = createApp(wrappedComponent);
3275

3376
if (this.element.__vue_app__ !== undefined) {
3477
this.element.__vue_app__.unmount();
@@ -62,4 +105,25 @@ export default class extends Controller<Element & { __vue_app__?: App<Element> }
62105
private dispatchEvent(name: string, payload: any) {
63106
this.dispatch(name, { detail: payload, prefix: 'vue' });
64107
}
108+
109+
private wrapComponent(component: Component): Component {
110+
return defineComponent({
111+
setup: () => {
112+
const props = this.props;
113+
114+
return () =>
115+
h(component, {
116+
...props,
117+
...Object.fromEntries(
118+
Object.keys(props).map((propName) => [
119+
`onUpdate:${propName}`,
120+
(value: unknown) => {
121+
props[propName] = value;
122+
},
123+
])
124+
),
125+
});
126+
},
127+
});
128+
}
65129
}

0 commit comments

Comments
 (0)