Skip to content

Commit bd4b700

Browse files
committed
Make RenderController are reactive
1 parent 781f66c commit bd4b700

File tree

3 files changed

+132
-10
lines changed

3 files changed

+132
-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: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,41 @@
11
import { Controller } from '@hotwired/stimulus';
2-
import { createApp } from 'vue';
2+
import { createApp, defineComponent, h, shallowReactive, toRaw, toRefs, unref, watch } 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(
10+
(propName) => !Object.prototype.hasOwnProperty.call(newProps, propName)
11+
);
12+
}
13+
removedPropNames.forEach((propName) => {
14+
delete this.props[propName];
15+
});
16+
}
17+
if (newProps) {
18+
Object.entries(newProps).forEach(([propName, propValue]) => {
19+
this.props[propName] = propValue;
20+
});
21+
}
22+
}
23+
initialize() {
24+
const props = this.hasPropsValue && this.propsValue ? this.propsValue : {};
25+
this.props = shallowReactive({ ...props });
26+
watch(
27+
this.props,
28+
(props) => {
29+
this.propsValue = toRaw(props);
30+
},
31+
{ flush: 'post' }
32+
);
33+
}
534
connect() {
6-
this.props = this.propsValue ?? null;
735
this.dispatchEvent('connect', { componentName: this.componentValue, props: this.props });
836
const component = window.resolveVueComponent(this.componentValue);
9-
this.app = createApp(component, this.props);
37+
const wrappedComponent = this.wrapComponent(component);
38+
this.app = createApp(wrappedComponent);
1039
if (this.element.__vue_app__ !== undefined) {
1140
this.element.__vue_app__.unmount();
1241
}
@@ -33,6 +62,27 @@ class default_1 extends Controller {
3362
dispatchEvent(name, payload) {
3463
this.dispatch(name, { detail: payload, prefix: 'vue' });
3564
}
65+
wrapComponent(component) {
66+
return defineComponent({
67+
setup: () => {
68+
const propsRefs = toRefs(this.props);
69+
return () =>
70+
h(component, {
71+
...Object.fromEntries(
72+
Object.entries(propsRefs).map(([propName, propRef]) => [propName, unref(propRef)])
73+
),
74+
...Object.fromEntries(
75+
Object.keys(propsRefs).map((propName) => [
76+
`onUpdate:${propName}`,
77+
(value) => {
78+
propsRefs[propName].value = value;
79+
},
80+
])
81+
),
82+
});
83+
},
84+
});
85+
}
3686
}
3787
default_1.values = {
3888
component: String,

src/Vue/assets/src/render_controller.ts

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,72 @@
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+
toRefs,
21+
unref,
22+
watch,
23+
} from 'vue';
1224

1325
export default class extends Controller<Element & { __vue_app__?: App<Element> }> {
14-
private props: Record<string, unknown> | null;
26+
private props: ShallowReactive<Record<string, unknown>>;
1527
private app: App<Element>;
1628
declare readonly componentValue: string;
17-
declare readonly propsValue: Record<string, unknown> | null | undefined;
29+
declare readonly hasPropsValue: boolean;
30+
declare propsValue: Record<string, unknown> | null | undefined;
1831

1932
static values = {
2033
component: String,
2134
props: Object,
2235
};
2336

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

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

2973
const component = window.resolveVueComponent(this.componentValue);
74+
const wrappedComponent = this.wrapComponent(component);
3075

31-
this.app = createApp(component, this.props);
76+
this.app = createApp(wrappedComponent);
3277

3378
if (this.element.__vue_app__ !== undefined) {
3479
this.element.__vue_app__.unmount();
@@ -62,4 +107,27 @@ export default class extends Controller<Element & { __vue_app__?: App<Element> }
62107
private dispatchEvent(name: string, payload: any) {
63108
this.dispatch(name, { detail: payload, prefix: 'vue' });
64109
}
110+
111+
private wrapComponent(component: Component): Component {
112+
return defineComponent({
113+
setup: () => {
114+
const propsRefs = toRefs(this.props);
115+
116+
return () =>
117+
h(component, {
118+
...Object.fromEntries(
119+
Object.entries(propsRefs).map(([propName, propRef]) => [propName, unref(propRef)])
120+
),
121+
...Object.fromEntries(
122+
Object.keys(propsRefs).map((propName) => [
123+
`onUpdate:${propName}`,
124+
(value: unknown) => {
125+
propsRefs[propName].value = value;
126+
},
127+
])
128+
),
129+
});
130+
},
131+
});
132+
}
65133
}

0 commit comments

Comments
 (0)