turbo-mount 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -4
- data/README.md +94 -66
- data/app/assets/javascripts/turbo-mount/react.js +8 -19
- data/app/assets/javascripts/turbo-mount/react.min.js +1 -1
- data/app/assets/javascripts/turbo-mount/react.min.js.map +1 -1
- data/app/assets/javascripts/turbo-mount/svelte.js +8 -19
- data/app/assets/javascripts/turbo-mount/svelte.min.js +1 -1
- data/app/assets/javascripts/turbo-mount/svelte.min.js.map +1 -1
- data/app/assets/javascripts/turbo-mount/vue.js +8 -19
- data/app/assets/javascripts/turbo-mount/vue.min.js +1 -1
- data/app/assets/javascripts/turbo-mount/vue.min.js.map +1 -1
- data/app/assets/javascripts/turbo-mount.js +53 -26
- data/app/assets/javascripts/turbo-mount.min.js +1 -1
- data/app/assets/javascripts/turbo-mount.min.js.map +1 -1
- data/lib/generators/turbo_mount/install/turbo-mount.js.tt +18 -0
- data/lib/generators/turbo_mount/install_generator.rb +112 -0
- data/lib/turbo/mount/helpers.rb +3 -14
- data/lib/turbo/mount/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3565dc0d8e80907e11600573cfb18049fe5360c9551facefb9a22d871bebad2e
|
4
|
+
data.tar.gz: 2e82006304c22c3b663511abbefaee3a0fc8e96aa972b044e1fca640cfd9c262
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2490534f21cf102b00c00524457be4b6b6f3d048e2d0477ad20bdbf2818704d5b39580bc9f71d5f33de6f39fcdd97faa20e0c0cc05a3c338225091e1096fa428
|
7
|
+
data.tar.gz: 6154769b9a73b2bf3e65e0c6a395a154109466d698f79dc5483378ed7f68cce060c41183725f240fff942a3cf09e03ac909b2f0d0f0da70a574a6712e28e1251
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning].
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.3.0] - 2024-05-31
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Installation script. ([@skryukov])
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- [BREAKING] New API without controller inheritance. ([@skryukov])
|
19
|
+
To migrate to the new API:
|
20
|
+
- Replace `new TurboMountReact()` (or any other framework specific constructor) with `new TurboMount()`
|
21
|
+
- Replace `turboMount.register(...)` with `registerComponent(turboMount, ...)`
|
22
|
+
- Replace `turbo_mount_react_component` (or any other framework specific helper) with `turbo_mount`
|
23
|
+
- Also see the new API for plugins and custom controllers in the README.
|
24
|
+
|
10
25
|
## [0.2.3] - 2024-05-12
|
11
26
|
|
12
27
|
### Added
|
@@ -36,10 +51,11 @@ and this project adheres to [Semantic Versioning].
|
|
36
51
|
|
37
52
|
[@skryukov]: https://github.com/skryukov
|
38
53
|
|
39
|
-
[Unreleased]: https://github.com/skryukov/turbo-mount/compare/v0.
|
40
|
-
[0.
|
41
|
-
[0.2.
|
42
|
-
[0.2.
|
54
|
+
[Unreleased]: https://github.com/skryukov/turbo-mount/compare/v0.3.0...HEAD
|
55
|
+
[0.3.0]: https://github.com/skryukov/turbo-mount/compare/v0.2.3...v0.3.0
|
56
|
+
[0.2.3]: https://github.com/skryukov/turbo-mount/compare/v0.2.2...v0.2.3
|
57
|
+
[0.2.2]: https://github.com/skryukov/turbo-mount/compare/v0.2.0...v0.2.2
|
58
|
+
[0.2.0]: https://github.com/skryukov/turbo-mount/compare/v0.1.0...v0.2.0
|
43
59
|
[0.1.0]: https://github.com/skryukov/turbo-mount/commits/v0.1.0
|
44
60
|
|
45
61
|
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
data/README.md
CHANGED
@@ -1,104 +1,143 @@
|
|
1
|
-
# Turbo
|
1
|
+
# Turbo Mount
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/turbo-mount)
|
4
4
|
|
5
|
-
`
|
5
|
+
`TurboMount` is a simple library that allows you to add highly interactive components from React, Vue, Svelte, and other frameworks to your Hotwire application.
|
6
|
+
|
7
|
+
## Table of Contents
|
8
|
+
- [Installation](#installation)
|
9
|
+
- [Importmaps](#importmaps)
|
10
|
+
- [Usage](#usage)
|
11
|
+
- [Initialization](#initialization)
|
12
|
+
- [Standard Initialization](#standard-initialization)
|
13
|
+
- [Simplified Initialization](#simplified-initialization)
|
14
|
+
- [Plugin-Specific Initialization](#plugin-specific-initialization)
|
15
|
+
- [View Helpers](#view-helpers)
|
16
|
+
- [Supported Frameworks](#supported-frameworks)
|
17
|
+
- [Custom Controllers](#custom-controllers)
|
18
|
+
- [Vite Integration](#vite-integration)
|
19
|
+
- [Mount Target](#mount-target)
|
20
|
+
- [License](#license)
|
21
|
+
|
22
|
+
<a href="https://evilmartians.com/?utm_source=turbo-mount&utm_campaign=project_page">
|
23
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Built by Evil Martians" width="236" height="54">
|
24
|
+
</a>
|
6
25
|
|
7
26
|
## Installation
|
8
27
|
|
9
|
-
|
28
|
+
To install Turbo Mount, add the following line to your `Gemfile` and run `bundle install`:
|
10
29
|
|
11
30
|
```ruby
|
12
31
|
gem "turbo-mount"
|
13
32
|
```
|
14
33
|
|
15
|
-
|
34
|
+
### Automatic Installation
|
35
|
+
|
36
|
+
Run the following command to install the necessary files:
|
16
37
|
|
17
38
|
```bash
|
18
|
-
|
19
|
-
# or with yarn
|
20
|
-
yarn add turbo-mount
|
39
|
+
bin/rails generate turbo_mount:install
|
21
40
|
```
|
22
41
|
|
23
|
-
|
42
|
+
This will add `turbo-mount` package and framework dependencies to your `package.json` or `importmap.rb`, and create the Turbo Mount initialization file.
|
24
43
|
|
25
|
-
###
|
44
|
+
### Manual Installation
|
26
45
|
|
27
|
-
|
46
|
+
You can also install the necessary JavaScript files manually.
|
28
47
|
|
29
|
-
|
48
|
+
If your project utilizes build tools such as [Vite](http://vite-ruby.netlify.app), also install the `turbo-mount` package:
|
30
49
|
|
31
|
-
|
50
|
+
```bash
|
51
|
+
npm install turbo-mount
|
52
|
+
# or with yarn
|
53
|
+
yarn add turbo-mount
|
32
54
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
55
|
+
# and the desired framework
|
56
|
+
npm install react react-dom
|
57
|
+
# or
|
58
|
+
npm install vue
|
59
|
+
# or
|
60
|
+
npm install svelte
|
61
|
+
```
|
62
|
+
|
63
|
+
If you're using Vite, don't forget to add [framework-specific plugins](https://vitejs.dev/plugins) to your `vite.config.js`.
|
38
64
|
|
39
|
-
|
40
|
-
|
65
|
+
### Importmaps
|
66
|
+
To use `TurboMount` with importmaps, you need to pin the necessary JavaScript files in your `config/importmap.rb`:
|
41
67
|
|
42
|
-
|
68
|
+
```ruby
|
69
|
+
pin "turbo-mount", to: "turbo-mount.min.js"
|
70
|
+
pin "turbo-mount/react", to: "turbo-mount/react.min.js"
|
43
71
|
```
|
44
72
|
|
45
|
-
|
73
|
+
This ensures that `turbo-mount` and its plugins are available in your application.
|
46
74
|
|
47
|
-
|
75
|
+
Also pin the desired framework:
|
48
76
|
|
49
|
-
```
|
50
|
-
|
51
|
-
|
52
|
-
|
77
|
+
```bash
|
78
|
+
bin/importmap pin react react-dom react-dom/client
|
79
|
+
# or
|
80
|
+
bin/importmap pin vue
|
81
|
+
# or
|
82
|
+
bin/importmap pin svelte
|
83
|
+
```
|
53
84
|
|
54
|
-
|
85
|
+
Note: Importmap-only mode is quite limited in terms of JavaScript dependencies. If you're using a more complex setup, consider using a bundler like Vite.
|
55
86
|
|
56
|
-
|
57
|
-
```
|
87
|
+
## Usage
|
58
88
|
|
59
|
-
|
89
|
+
### Initialization
|
60
90
|
|
61
|
-
|
91
|
+
To begin using `TurboMount`, start by initializing the library and registering the components you intend to use. Here's how to set it up with a React plugin:
|
62
92
|
|
63
93
|
```js
|
64
|
-
import {
|
65
|
-
import {
|
94
|
+
import { TurboMount } from "turbo-mount";
|
95
|
+
import { registerComponent } from "turbo-mount/react";
|
96
|
+
import { HexColorPicker } from 'react-colorful';
|
66
97
|
|
67
|
-
const turboMount = new
|
98
|
+
const turboMount = new TurboMount(); // or new TurboMount({ application })
|
68
99
|
|
69
|
-
turboMount
|
100
|
+
registerComponent(turboMount, "HexColorPicker", HexColorPicker);
|
70
101
|
```
|
71
102
|
|
103
|
+
If you prefer not to specify the `application` explicitly, `TurboMount` can automatically detect or initialize it. Turbo Mount uses the `window.Stimulus` if available; otherwise, it initializes a new Stimulus application.
|
104
|
+
|
72
105
|
### View Helpers
|
73
106
|
|
74
107
|
Use the following helpers to mount components in your views:
|
75
108
|
|
76
109
|
```erb
|
77
|
-
<%=
|
110
|
+
<%= turbo_mount("HexColorPicker", props: {color: "#034"}, class: "mb-5") %>
|
111
|
+
```
|
78
112
|
|
79
|
-
|
113
|
+
This will generate the following HTML:
|
80
114
|
|
81
|
-
|
115
|
+
```html
|
116
|
+
<div data-controller="turbo-mount-hex-color-picker"
|
117
|
+
data-turbo-mount-hex-color-picker-component-value="HexColorPicker"
|
118
|
+
data-turbo-mount-hex-color-picker-props-value="{"color":"#034"}"
|
119
|
+
class="mb-5">
|
120
|
+
</div>
|
82
121
|
```
|
83
122
|
|
84
123
|
### Supported Frameworks
|
85
124
|
|
86
125
|
`TurboMount` supports the following frameworks:
|
87
126
|
|
88
|
-
- React `"turbo-mount/react"`
|
89
|
-
- Vue `"turbo-mount/vue"`
|
90
|
-
- Svelte `"turbo-mount/svelte"`
|
127
|
+
- React: `"turbo-mount/react"`
|
128
|
+
- Vue: `"turbo-mount/vue"`
|
129
|
+
- Svelte: `"turbo-mount/svelte"`
|
91
130
|
|
92
|
-
|
131
|
+
To add support for other frameworks, create a custom plugin. See included plugins for examples.
|
93
132
|
|
94
133
|
### Custom Controllers
|
95
134
|
|
96
135
|
To customize component behavior or pass functions as props, create a custom controller:
|
97
136
|
|
98
137
|
```js
|
99
|
-
import {
|
138
|
+
import { TurboMountController } from "turbo-mount";
|
100
139
|
|
101
|
-
export default class extends
|
140
|
+
export default class extends TurboMountController {
|
102
141
|
get componentProps() {
|
103
142
|
return {
|
104
143
|
...this.propsValue,
|
@@ -107,62 +146,51 @@ export default class extends TurboMountReactController {
|
|
107
146
|
}
|
108
147
|
|
109
148
|
onChange = (color) => {
|
110
|
-
this.propsValue = { ...this.propsValue, color
|
149
|
+
// same as this.propsValue = { ...this.propsValue, color };
|
150
|
+
// but skips the rerendering of the component:
|
151
|
+
this.componentProps = { ...this.propsValue, color };
|
111
152
|
};
|
112
153
|
}
|
113
154
|
```
|
114
155
|
|
115
|
-
Then pass this controller the
|
156
|
+
Then pass this controller to the `registerComponent` method:
|
116
157
|
|
117
158
|
```js
|
118
|
-
import
|
159
|
+
import HexColorPickerController from "controllers/turbo_mount/hex_color_picker_controller";
|
119
160
|
|
120
|
-
turboMount
|
161
|
+
registerComponent(turboMount, "HexColorPicker", HexColorPicker, HexColorPickerController);
|
121
162
|
```
|
122
163
|
|
123
164
|
### Vite Integration
|
124
165
|
|
125
|
-
`TurboMount` includes a `registerComponents` function that automates the loading of components (requires `stimulus-vite-helpers` package). It also accepts an optional `controllers` property to autoload customized controllers:
|
166
|
+
`TurboMount` includes a `registerComponents` function that automates the loading of components (requires the `stimulus-vite-helpers` package). It also accepts an optional `controllers` property to autoload customized controllers:
|
126
167
|
|
127
168
|
```js
|
128
169
|
import { TurboMount } from "turbo-mount/react";
|
129
|
-
import { registerComponents } from "turbo-mount/
|
170
|
+
import { registerComponents } from "turbo-mount/registerComponents/react";
|
130
171
|
|
131
172
|
const controllers = import.meta.glob("./**/*_controller.js", { eager: true });
|
132
|
-
const components = import.meta.glob(
|
173
|
+
const components = import.meta.glob("/components/**/*.jsx", { eager: true });
|
133
174
|
|
134
175
|
const turboMount = new TurboMount();
|
135
176
|
registerComponents({ turboMount, components, controllers });
|
136
177
|
```
|
137
178
|
|
138
179
|
The `registerComponents` helper searches for controllers in the following paths:
|
139
|
-
- `controllers/turbo-mount/${framework}/${controllerName}`
|
140
|
-
- `controllers/turbo-mount/${framework}-${controllerName}`
|
141
|
-
- `controllers/turbo-mount-${framework}-${controllerName}`
|
142
180
|
- `controllers/turbo-mount/${controllerName}`
|
143
181
|
- `controllers/turbo-mount-${controllerName}`
|
144
182
|
|
145
|
-
### Mount
|
183
|
+
### Mount Target
|
146
184
|
|
147
185
|
To specify a non-root mount target, use the `data-<%= controller_name %>-target="mount"` attribute:
|
148
186
|
|
149
187
|
```erb
|
150
|
-
<%=
|
188
|
+
<%= turbo_mount("HexColorPicker", props: {color: "#430"}) do |controller_name| %>
|
151
189
|
<h3>Color picker</h3>
|
152
190
|
<div data-<%= controller_name %>-target="mount"></div>
|
153
191
|
<% end %>
|
154
192
|
```
|
155
193
|
|
156
|
-
## Development
|
157
|
-
|
158
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
159
|
-
|
160
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
161
|
-
|
162
|
-
## Contributing
|
163
|
-
|
164
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/skryukov/turbo-mount.
|
165
|
-
|
166
194
|
## License
|
167
195
|
|
168
196
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -1,29 +1,18 @@
|
|
1
|
-
import {
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
2
3
|
import { createElement } from 'react';
|
3
4
|
import { createRoot } from 'react-dom/client';
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
this.framework = "react";
|
9
|
-
}
|
10
|
-
mountComponent(el, Component, props) {
|
6
|
+
const plugin = {
|
7
|
+
mountComponent: (mountProps) => {
|
8
|
+
const { el, Component, props } = mountProps;
|
11
9
|
const root = createRoot(el);
|
12
10
|
root.render(createElement(Component, props));
|
13
11
|
return () => {
|
14
12
|
root.unmount();
|
15
13
|
};
|
16
|
-
}
|
17
|
-
}
|
18
|
-
|
19
|
-
const plugin = {
|
20
|
-
framework: "react",
|
21
|
-
controller: TurboMountReactController,
|
14
|
+
},
|
22
15
|
};
|
23
|
-
|
24
|
-
constructor(props) {
|
25
|
-
super(Object.assign(Object.assign({}, props), { plugin }));
|
26
|
-
}
|
27
|
-
}
|
16
|
+
const registerComponent = buildRegisterFunction(plugin);
|
28
17
|
|
29
|
-
export {
|
18
|
+
export { plugin as default, registerComponent };
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";import{createElement as t}from"react";import{createRoot as r}from"react-dom/client";const n={mountComponent:o=>{const{el:n,Component:m,props:e}=o,u=r(n);return u.render(t(m,e)),()=>{u.unmount()}}},m=o(n);export{n as default,m as registerComponent};
|
2
2
|
//# sourceMappingURL=react.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"react.min.js","sources":["../../src/plugins/react/index.ts"
|
1
|
+
{"version":3,"file":"react.min.js","sources":["../../src/plugins/react/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport { ComponentType, createElement } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nconst plugin: Plugin<ComponentType> = {\n mountComponent: (mountProps) => {\n const { el, Component, props } = mountProps;\n const root = createRoot(el);\n root.render(createElement(Component, props));\n\n return () => {\n root.unmount();\n };\n },\n};\n\nconst registerComponent = buildRegisterFunction(plugin);\n\nexport { TurboMount, registerComponent };\n\nexport default plugin;\n"],"names":["plugin","mountComponent","mountProps","el","Component","props","root","createRoot","render","createElement","unmount","registerComponent","buildRegisterFunction"],"mappings":"4KAIA,MAAMA,EAAgC,CACpCC,eAAiBC,IACf,MAAMC,GAAEA,EAAEC,UAAEA,EAASC,MAAEA,GAAUH,EAC3BI,EAAOC,EAAWJ,GAGxB,OAFAG,EAAKE,OAAOC,EAAcL,EAAWC,IAE9B,KACLC,EAAKI,SAAS,CACf,GAICC,EAAoBC,EAAsBZ"}
|
@@ -1,26 +1,15 @@
|
|
1
|
-
import {
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
this.framework = "svelte";
|
7
|
-
}
|
8
|
-
mountComponent(el, Component, props) {
|
4
|
+
const plugin = {
|
5
|
+
mountComponent: (mountProps) => {
|
6
|
+
const { el, Component, props } = mountProps;
|
9
7
|
const component = new Component({ target: el, props });
|
10
8
|
return () => {
|
11
9
|
component.$destroy();
|
12
10
|
};
|
13
|
-
}
|
14
|
-
}
|
15
|
-
|
16
|
-
const plugin = {
|
17
|
-
framework: "svelte",
|
18
|
-
controller: TurboMountSvelteController,
|
11
|
+
},
|
19
12
|
};
|
20
|
-
|
21
|
-
constructor(props) {
|
22
|
-
super(Object.assign(Object.assign({}, props), { plugin }));
|
23
|
-
}
|
24
|
-
}
|
13
|
+
const registerComponent = buildRegisterFunction(plugin);
|
25
14
|
|
26
|
-
export {
|
15
|
+
export { plugin as default, registerComponent };
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";const t={mountComponent:o=>{const{el:t,Component:r,props:n}=o,e=new r({target:t,props:n});return()=>{e.$destroy()}}},r=o(t);export{t as default,r as registerComponent};
|
2
2
|
//# sourceMappingURL=svelte.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"svelte.min.js","sources":["../../src/plugins/svelte/index.ts"
|
1
|
+
{"version":3,"file":"svelte.min.js","sources":["../../src/plugins/svelte/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport { ComponentType } from \"svelte\";\n\nconst plugin: Plugin<ComponentType> = {\n mountComponent: (mountProps) => {\n const { el, Component, props } = mountProps;\n const component = new Component({ target: el, props });\n\n return () => {\n component.$destroy();\n };\n },\n};\n\nconst registerComponent = buildRegisterFunction(plugin);\n\nexport { TurboMount, registerComponent };\n\nexport default plugin;\n"],"names":["plugin","mountComponent","mountProps","el","Component","props","component","target","$destroy","registerComponent","buildRegisterFunction"],"mappings":"wFAGA,MAAMA,EAAgC,CACpCC,eAAiBC,IACf,MAAMC,GAAEA,EAAEC,UAAEA,EAASC,MAAEA,GAAUH,EAC3BI,EAAY,IAAIF,EAAU,CAAEG,OAAQJ,EAAIE,UAE9C,MAAO,KACLC,EAAUE,UAAU,CACrB,GAICC,EAAoBC,EAAsBV"}
|
@@ -1,28 +1,17 @@
|
|
1
|
-
import {
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
2
3
|
import { createApp } from 'vue';
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
this.framework = "vue";
|
8
|
-
}
|
9
|
-
mountComponent(el, Component, props) {
|
5
|
+
const plugin = {
|
6
|
+
mountComponent: (mountProps) => {
|
7
|
+
const { el, Component, props } = mountProps;
|
10
8
|
const app = createApp(Component, props);
|
11
9
|
app.mount(el);
|
12
10
|
return () => {
|
13
11
|
app.unmount();
|
14
12
|
};
|
15
|
-
}
|
16
|
-
}
|
17
|
-
|
18
|
-
const plugin = {
|
19
|
-
framework: "vue",
|
20
|
-
controller: TurboMountVueController,
|
13
|
+
},
|
21
14
|
};
|
22
|
-
|
23
|
-
constructor(props) {
|
24
|
-
super(Object.assign(Object.assign({}, props), { plugin }));
|
25
|
-
}
|
26
|
-
}
|
15
|
+
const registerComponent = buildRegisterFunction(plugin);
|
27
16
|
|
28
|
-
export {
|
17
|
+
export { plugin as default, registerComponent };
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import{buildRegisterFunction as o}from"turbo-mount";export{TurboMount}from"turbo-mount";import{createApp as t}from"vue";const n={mountComponent:o=>{const{el:n,Component:r,props:u}=o,m=t(r,u);return m.mount(n),()=>{m.unmount()}}},r=o(n);export{n as default,r as registerComponent};
|
2
2
|
//# sourceMappingURL=vue.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"vue.min.js","sources":["../../src/plugins/vue/index.ts"
|
1
|
+
{"version":3,"file":"vue.min.js","sources":["../../src/plugins/vue/index.ts"],"sourcesContent":["import { buildRegisterFunction, Plugin, TurboMount } from \"turbo-mount\";\nimport { App, createApp } from \"vue\";\n\nconst plugin: Plugin<App> = {\n mountComponent: (mountProps) => {\n const { el, Component, props } = mountProps;\n const app = createApp(Component, props as Record<string, unknown>);\n app.mount(el);\n\n return () => {\n app.unmount();\n };\n },\n};\n\nconst registerComponent = buildRegisterFunction(plugin);\n\nexport { TurboMount, registerComponent };\n\nexport default plugin;\n"],"names":["plugin","mountComponent","mountProps","el","Component","props","app","createApp","mount","unmount","registerComponent","buildRegisterFunction"],"mappings":"wHAGA,MAAMA,EAAsB,CAC1BC,eAAiBC,IACf,MAAMC,GAAEA,EAAEC,UAAEA,EAASC,MAAEA,GAAUH,EAC3BI,EAAMC,EAAUH,EAAWC,GAGjC,OAFAC,EAAIE,MAAML,GAEH,KACLG,EAAIG,SAAS,CACd,GAICC,EAAoBC,EAAsBX"}
|
@@ -1,6 +1,10 @@
|
|
1
1
|
import { Controller, Application } from '@hotwired/stimulus';
|
2
2
|
|
3
3
|
class TurboMountController extends Controller {
|
4
|
+
constructor() {
|
5
|
+
super(...arguments);
|
6
|
+
this.skipPropsChangeCallback = false;
|
7
|
+
}
|
4
8
|
connect() {
|
5
9
|
this._umountComponentCallback || (this._umountComponentCallback = this.mountComponent(this.mountElement, this.resolvedComponent, this.componentProps));
|
6
10
|
}
|
@@ -8,6 +12,10 @@ class TurboMountController extends Controller {
|
|
8
12
|
this.umountComponent();
|
9
13
|
}
|
10
14
|
propsValueChanged() {
|
15
|
+
if (this.skipPropsChangeCallback) {
|
16
|
+
this.skipPropsChangeCallback = false;
|
17
|
+
return;
|
18
|
+
}
|
11
19
|
this.umountComponent();
|
12
20
|
this._umountComponentCallback || (this._umountComponentCallback = this.mountComponent(this.mountElement, this.resolvedComponent, this.componentProps));
|
13
21
|
}
|
@@ -18,15 +26,25 @@ class TurboMountController extends Controller {
|
|
18
26
|
return this.hasMountTarget ? this.mountTarget : this.element;
|
19
27
|
}
|
20
28
|
get resolvedComponent() {
|
21
|
-
return this.
|
29
|
+
return this.resolveMounted(this.componentValue).component;
|
30
|
+
}
|
31
|
+
get resolvedPlugin() {
|
32
|
+
return this.resolveMounted(this.componentValue).plugin;
|
22
33
|
}
|
23
34
|
umountComponent() {
|
24
35
|
this._umountComponentCallback && this._umountComponentCallback();
|
25
36
|
this._umountComponentCallback = undefined;
|
26
37
|
}
|
27
|
-
|
38
|
+
mountComponent(el, Component, props) {
|
39
|
+
return this.resolvedPlugin.mountComponent({ el, Component, props });
|
40
|
+
}
|
41
|
+
resolveMounted(component) {
|
28
42
|
const app = this.application;
|
29
|
-
return app.turboMount
|
43
|
+
return app.turboMount.resolve(component);
|
44
|
+
}
|
45
|
+
setComponentProps(props) {
|
46
|
+
this.skipPropsChangeCallback = true;
|
47
|
+
this.propsValue = props;
|
30
48
|
}
|
31
49
|
}
|
32
50
|
TurboMountController.values = {
|
@@ -40,34 +58,30 @@ const camelToKebabCase = (str) => {
|
|
40
58
|
};
|
41
59
|
|
42
60
|
class TurboMount {
|
43
|
-
constructor(
|
44
|
-
var _a;
|
61
|
+
constructor(props = {}) {
|
45
62
|
this.components = new Map();
|
46
|
-
this.application = this.findOrStartApplication(application);
|
47
|
-
this.
|
48
|
-
this.
|
49
|
-
(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
this.application = this.findOrStartApplication(props.application);
|
64
|
+
this.application.turboMount = this;
|
65
|
+
this.application.register("turbo-mount", TurboMountController);
|
66
|
+
document.addEventListener("turbo:before-morph-element", (event) => {
|
67
|
+
var _a;
|
68
|
+
const turboMorphEvent = event;
|
69
|
+
const { target, detail } = turboMorphEvent;
|
70
|
+
if ((_a = target.getAttribute("data-controller")) === null || _a === void 0 ? void 0 : _a.includes("turbo-mount")) {
|
71
|
+
target.setAttribute("data-turbo-mount-props-value", detail.newElement.getAttribute("data-turbo-mount-props-value") ||
|
72
|
+
"{}");
|
73
|
+
event.preventDefault();
|
74
|
+
}
|
75
|
+
});
|
54
76
|
}
|
55
|
-
|
56
|
-
|
57
|
-
if (!application) {
|
58
|
-
application = Application.start();
|
59
|
-
window.Stimulus = application;
|
60
|
-
}
|
61
|
-
return application;
|
62
|
-
}
|
63
|
-
register(name, component, controller) {
|
64
|
-
controller || (controller = this.baseController);
|
77
|
+
register(plugin, name, component, controller) {
|
78
|
+
controller || (controller = TurboMountController);
|
65
79
|
if (this.components.has(name)) {
|
66
80
|
throw new Error(`Component '${name}' is already registered.`);
|
67
81
|
}
|
68
|
-
this.components.set(name, component);
|
82
|
+
this.components.set(name, { component, plugin });
|
69
83
|
if (controller) {
|
70
|
-
const controllerName = `turbo-mount-${
|
84
|
+
const controllerName = `turbo-mount-${camelToKebabCase(name)}`;
|
71
85
|
this.application.register(controllerName, controller);
|
72
86
|
}
|
73
87
|
}
|
@@ -78,6 +92,19 @@ class TurboMount {
|
|
78
92
|
}
|
79
93
|
return component;
|
80
94
|
}
|
95
|
+
findOrStartApplication(hydratedApp) {
|
96
|
+
let application = hydratedApp || window.Stimulus;
|
97
|
+
if (!application) {
|
98
|
+
application = Application.start();
|
99
|
+
window.Stimulus = application;
|
100
|
+
}
|
101
|
+
return application;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
function buildRegisterFunction(plugin) {
|
105
|
+
return (turboMount, name, component, controller) => {
|
106
|
+
turboMount.register(plugin, name, component, controller);
|
107
|
+
};
|
81
108
|
}
|
82
109
|
|
83
|
-
export { TurboMount, TurboMountController };
|
110
|
+
export { TurboMount, TurboMountController, buildRegisterFunction };
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{Controller as t,Application as o}from"@hotwired/stimulus";class n extends t{connect(){this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps))}disconnect(){this.umountComponent()}propsValueChanged(){this.umountComponent(),this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps))}get componentProps(){return this.propsValue}get mountElement(){return this.hasMountTarget?this.mountTarget:this.element}get resolvedComponent(){return this.
|
1
|
+
import{Controller as t,Application as o}from"@hotwired/stimulus";class n extends t{constructor(){super(...arguments),this.skipPropsChangeCallback=!1}connect(){this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps))}disconnect(){this.umountComponent()}propsValueChanged(){this.skipPropsChangeCallback?this.skipPropsChangeCallback=!1:(this.umountComponent(),this._umountComponentCallback||(this._umountComponentCallback=this.mountComponent(this.mountElement,this.resolvedComponent,this.componentProps)))}get componentProps(){return this.propsValue}get mountElement(){return this.hasMountTarget?this.mountTarget:this.element}get resolvedComponent(){return this.resolveMounted(this.componentValue).component}get resolvedPlugin(){return this.resolveMounted(this.componentValue).plugin}umountComponent(){this._umountComponentCallback&&this._umountComponentCallback(),this._umountComponentCallback=void 0}mountComponent(t,o,n){return this.resolvedPlugin.mountComponent({el:t,Component:o,props:n})}resolveMounted(t){return this.application.turboMount.resolve(t)}setComponentProps(t){this.skipPropsChangeCallback=!0,this.propsValue=t}}n.values={props:Object,component:String},n.targets=["mount"];class e{constructor(t={}){this.components=new Map,this.application=this.findOrStartApplication(t.application),this.application.turboMount=this,this.application.register("turbo-mount",n),document.addEventListener("turbo:before-morph-element",(t=>{var o;const n=t,{target:e,detail:s}=n;(null===(o=e.getAttribute("data-controller"))||void 0===o?void 0:o.includes("turbo-mount"))&&(e.setAttribute("data-turbo-mount-props-value",s.newElement.getAttribute("data-turbo-mount-props-value")||"{}"),t.preventDefault())}))}register(t,o,e,s){if(s||(s=n),this.components.has(o))throw new Error(`Component '${o}' is already registered.`);if(this.components.set(o,{component:e,plugin:t}),s){const t=`turbo-mount-${r=o,r.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}`;this.application.register(t,s)}var r}resolve(t){const o=this.components.get(t);if(!o)throw new Error(`Unknown component: ${t}`);return o}findOrStartApplication(t){let n=t||window.Stimulus;return n||(n=o.start(),window.Stimulus=n),n}}function s(t){return(o,n,e,s)=>{o.register(t,n,e,s)}}export{e as TurboMount,n as TurboMountController,s as buildRegisterFunction};
|
2
2
|
//# sourceMappingURL=turbo-mount.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"turbo-mount.min.js","sources":["../src/turbo-mount-controller.ts","../src/turbo-mount.ts","../src/helpers.ts"],"sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\nimport { ApplicationWithTurboMount } from \"./turbo-mount\";\n\nexport
|
1
|
+
{"version":3,"file":"turbo-mount.min.js","sources":["../src/turbo-mount-controller.ts","../src/turbo-mount.ts","../src/helpers.ts"],"sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\nimport { ApplicationWithTurboMount } from \"./turbo-mount\";\n\nexport class TurboMountController extends Controller {\n static values = {\n props: Object,\n component: String,\n };\n static targets = [\"mount\"];\n\n private skipPropsChangeCallback = false;\n\n declare propsValue: object;\n declare componentValue: string;\n declare readonly hasMountTarget: boolean;\n declare readonly mountTarget: Element;\n\n _umountComponentCallback?: () => void;\n\n connect() {\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n disconnect() {\n this.umountComponent();\n }\n\n propsValueChanged() {\n // Prevent re-mounting the component if the props are being set by the component itself\n if (this.skipPropsChangeCallback) {\n this.skipPropsChangeCallback = false;\n return;\n }\n\n this.umountComponent();\n this._umountComponentCallback ||= this.mountComponent(\n this.mountElement,\n this.resolvedComponent,\n this.componentProps,\n );\n }\n\n get componentProps() {\n return this.propsValue;\n }\n\n get mountElement() {\n return this.hasMountTarget ? this.mountTarget : this.element;\n }\n\n get resolvedComponent() {\n return this.resolveMounted(this.componentValue).component;\n }\n\n get resolvedPlugin() {\n return this.resolveMounted(this.componentValue).plugin;\n }\n\n umountComponent() {\n this._umountComponentCallback && this._umountComponentCallback();\n this._umountComponentCallback = undefined;\n }\n\n mountComponent(el: Element, Component: unknown, props: object) {\n return this.resolvedPlugin.mountComponent({ el, Component, props });\n }\n\n resolveMounted(component: string) {\n const app = this.application as ApplicationWithTurboMount;\n return app.turboMount.resolve(component);\n }\n\n setComponentProps(props: object) {\n this.skipPropsChangeCallback = true;\n this.propsValue = props;\n }\n}\n","import { Application, ControllerConstructor } from \"@hotwired/stimulus\";\n\nimport { camelToKebabCase } from \"./helpers\";\nimport { TurboMountController } from \"./turbo-mount-controller\";\n\ndeclare global {\n interface Window {\n Stimulus?: Application;\n }\n}\n\nexport interface ApplicationWithTurboMount extends Application {\n turboMount: TurboMount;\n}\n\nexport type MountComponentProps<T> = {\n el: Element;\n Component: T;\n props: object;\n};\n\nexport type Plugin<T> = {\n mountComponent: (props: MountComponentProps<T>) => () => void;\n};\n\nexport type TurboMountProps = {\n application?: Application;\n};\n\ntype TurboMountComponents<T> = Map<string, { component: T; plugin: Plugin<T> }>;\n\ninterface TurboMorphEvent extends CustomEvent {\n target: Element;\n detail: {\n newElement: Element;\n };\n}\n\nexport class TurboMount {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: TurboMountComponents<any>;\n application: ApplicationWithTurboMount;\n\n constructor(props: TurboMountProps = {}) {\n this.components = new Map();\n this.application = this.findOrStartApplication(props.application);\n this.application.turboMount = this;\n this.application.register(\"turbo-mount\", TurboMountController);\n\n document.addEventListener(\"turbo:before-morph-element\", (event) => {\n const turboMorphEvent = event as unknown as TurboMorphEvent;\n const { target, detail } = turboMorphEvent;\n\n if (target.getAttribute(\"data-controller\")?.includes(\"turbo-mount\")) {\n target.setAttribute(\n \"data-turbo-mount-props-value\",\n detail.newElement.getAttribute(\"data-turbo-mount-props-value\") ||\n \"{}\",\n );\n event.preventDefault();\n }\n });\n }\n\n register<T>(\n plugin: Plugin<T>,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) {\n controller ||= TurboMountController;\n if (this.components.has(name)) {\n throw new Error(`Component '${name}' is already registered.`);\n }\n this.components.set(name, { component, plugin });\n\n if (controller) {\n const controllerName = `turbo-mount-${camelToKebabCase(name)}`;\n this.application.register(controllerName, controller);\n }\n }\n\n resolve(name: string) {\n const component = this.components.get(name);\n if (!component) {\n throw new Error(`Unknown component: ${name}`);\n }\n return component;\n }\n\n private findOrStartApplication(hydratedApp?: Application) {\n let application = hydratedApp || window.Stimulus;\n\n if (!application) {\n application = Application.start();\n window.Stimulus = application;\n }\n return application as ApplicationWithTurboMount;\n }\n}\n\nexport function buildRegisterFunction<T>(plugin: Plugin<T>) {\n return (\n turboMount: TurboMount,\n name: string,\n component: T,\n controller?: ControllerConstructor,\n ) => {\n turboMount.register(plugin, name, component, controller);\n };\n}\n","export const camelToKebabCase = (str: string) => {\n return str.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n};\n"],"names":["TurboMountController","Controller","constructor","this","skipPropsChangeCallback","connect","_umountComponentCallback","mountComponent","mountElement","resolvedComponent","componentProps","disconnect","umountComponent","propsValueChanged","propsValue","hasMountTarget","mountTarget","element","resolveMounted","componentValue","component","resolvedPlugin","plugin","undefined","el","Component","props","application","turboMount","resolve","setComponentProps","values","Object","String","targets","TurboMount","components","Map","findOrStartApplication","register","document","addEventListener","event","turboMorphEvent","target","detail","_a","getAttribute","includes","setAttribute","newElement","preventDefault","name","controller","has","Error","set","controllerName","str","replace","toLowerCase","get","hydratedApp","window","Stimulus","Application","start","buildRegisterFunction"],"mappings":"iEAGM,MAAOA,UAA6BC,EAA1C,WAAAC,uBAOUC,KAAuBC,yBAAG,CAsEnC,CA7DC,OAAAC,GACEF,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,gBAER,CAED,UAAAC,GACER,KAAKS,iBACN,CAED,iBAAAC,GAEMV,KAAKC,wBACPD,KAAKC,yBAA0B,GAIjCD,KAAKS,kBACLT,KAAKG,2BAALH,KAAKG,yBAA6BH,KAAKI,eACrCJ,KAAKK,aACLL,KAAKM,kBACLN,KAAKO,iBAER,CAED,kBAAIA,GACF,OAAOP,KAAKW,UACb,CAED,gBAAIN,GACF,OAAOL,KAAKY,eAAiBZ,KAAKa,YAAcb,KAAKc,OACtD,CAED,qBAAIR,GACF,OAAON,KAAKe,eAAef,KAAKgB,gBAAgBC,SACjD,CAED,kBAAIC,GACF,OAAOlB,KAAKe,eAAef,KAAKgB,gBAAgBG,MACjD,CAED,eAAAV,GACET,KAAKG,0BAA4BH,KAAKG,2BACtCH,KAAKG,8BAA2BiB,CACjC,CAED,cAAAhB,CAAeiB,EAAaC,EAAoBC,GAC9C,OAAOvB,KAAKkB,eAAed,eAAe,CAAEiB,KAAIC,YAAWC,SAC5D,CAED,cAAAR,CAAeE,GAEb,OADYjB,KAAKwB,YACNC,WAAWC,QAAQT,EAC/B,CAED,iBAAAU,CAAkBJ,GAChBvB,KAAKC,yBAA0B,EAC/BD,KAAKW,WAAaY,CACnB,EA3EM1B,EAAA+B,OAAS,CACdL,MAAOM,OACPZ,UAAWa,QAENjC,EAAAkC,QAAU,CAAC,eC8BPC,EAKX,WAAAjC,CAAYwB,EAAyB,IACnCvB,KAAKiC,WAAa,IAAIC,IACtBlC,KAAKwB,YAAcxB,KAAKmC,uBAAuBZ,EAAMC,aACrDxB,KAAKwB,YAAYC,WAAazB,KAC9BA,KAAKwB,YAAYY,SAAS,cAAevC,GAEzCwC,SAASC,iBAAiB,8BAA+BC,UACvD,MAAMC,EAAkBD,GAClBE,OAAEA,EAAMC,OAAEA,GAAWF,GAEe,QAAtCG,EAAAF,EAAOG,aAAa,0BAAkB,IAAAD,OAAA,EAAAA,EAAEE,SAAS,kBACnDJ,EAAOK,aACL,+BACAJ,EAAOK,WAAWH,aAAa,iCAC7B,MAEJL,EAAMS,iBACP,GAEJ,CAED,QAAAZ,CACEjB,EACA8B,EACAhC,EACAiC,GAGA,GADAA,IAAAA,EAAerD,GACXG,KAAKiC,WAAWkB,IAAIF,GACtB,MAAM,IAAIG,MAAM,cAAcH,6BAIhC,GAFAjD,KAAKiC,WAAWoB,IAAIJ,EAAM,CAAEhC,YAAWE,WAEnC+B,EAAY,CACd,MAAMI,EAAiB,eC7EIC,ED6E4BN,EC5EpDM,EAAIC,QAAQ,kBAAmB,SAASC,gBD6E3CzD,KAAKwB,YAAYY,SAASkB,EAAgBJ,EAC3C,CC/E2B,IAACK,CDgF9B,CAED,OAAA7B,CAAQuB,GACN,MAAMhC,EAAYjB,KAAKiC,WAAWyB,IAAIT,GACtC,IAAKhC,EACH,MAAM,IAAImC,MAAM,sBAAsBH,KAExC,OAAOhC,CACR,CAEO,sBAAAkB,CAAuBwB,GAC7B,IAAInC,EAAcmC,GAAeC,OAAOC,SAMxC,OAJKrC,IACHA,EAAcsC,EAAYC,QAC1BH,OAAOC,SAAWrC,GAEbA,CACR,EAGG,SAAUwC,EAAyB7C,GACvC,MAAO,CACLM,EACAwB,EACAhC,EACAiC,KAEAzB,EAAWW,SAASjB,EAAQ8B,EAAMhC,EAAWiC,EAAW,CAE5D"}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { TurboMount } from "turbo-mount";
|
2
|
+
import { registerComponent } from "turbo-mount/<%= framework %>";
|
3
|
+
|
4
|
+
const turboMount = new TurboMount();
|
5
|
+
|
6
|
+
// to register a component use:
|
7
|
+
// registerComponent(turboMount, "Hello", Hello); // where Hello is the imported the component
|
8
|
+
|
9
|
+
// to override the default controller use:
|
10
|
+
// registerComponent(turboMount, "Hello", Hello, HelloController); // where HelloController is a Stimulus controller extended from TurboMountController
|
11
|
+
<%- if vite? -%>
|
12
|
+
|
13
|
+
// If you want to automatically register components use:
|
14
|
+
// import { registerComponents } from "turbo-mount/registerComponents/<%= framework %>";
|
15
|
+
// const controllers = import.meta.glob("/controllers/**/*_controller.js", { eager: true });
|
16
|
+
// const components = import.meta.glob("/components/**/*.jsx", { eager: true });
|
17
|
+
// registerComponents({ turboMount, components, controllers });
|
18
|
+
<%- end -%>
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TurboMount
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
FRAMEWORKS = {
|
7
|
+
"react" => {
|
8
|
+
pins: "react react-dom react-dom/client",
|
9
|
+
npm_packages: "react react-dom",
|
10
|
+
vite_plugin: "@vitejs/plugin-react"
|
11
|
+
},
|
12
|
+
"vue" => {
|
13
|
+
pins: "vue",
|
14
|
+
npm_packages: "vue",
|
15
|
+
vite_plugin: "@vitejs/plugin-vue"
|
16
|
+
},
|
17
|
+
"svelte" => {
|
18
|
+
pins: "svelte",
|
19
|
+
npm_packages: "svelte",
|
20
|
+
vite_plugin: "@sveltejs/vite-plugin-svelte"
|
21
|
+
}
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
source_root File.expand_path("install", __dir__)
|
25
|
+
|
26
|
+
def install
|
27
|
+
say "Installing Turbo Mount"
|
28
|
+
|
29
|
+
if build_tool.nil?
|
30
|
+
say "Could not find a package.json or config/importmap.rb file to add the turbo-mount dependency to, please add it manually.", :red
|
31
|
+
exit!
|
32
|
+
end
|
33
|
+
|
34
|
+
if importmap?
|
35
|
+
install_importmap
|
36
|
+
else
|
37
|
+
install_nodejs
|
38
|
+
end
|
39
|
+
|
40
|
+
say "Turbo Mount successfully installed", :green
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def install_nodejs
|
46
|
+
case build_tool
|
47
|
+
when "npm"
|
48
|
+
run "npm install turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
49
|
+
when "yarn"
|
50
|
+
run "yarn add turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
51
|
+
when "bun"
|
52
|
+
run "bun add turbo-mount #{FRAMEWORKS[framework][:npm_packages]}"
|
53
|
+
end
|
54
|
+
|
55
|
+
say "Creating Turbo Mount initializer"
|
56
|
+
template "turbo-mount.js", File.join("app/javascript/turbo-mount.js")
|
57
|
+
append_to_file "app/javascript/entrypoints/application.js", %(import "./turbo-mount"\n)
|
58
|
+
warn_about_vite_plugin if vite?
|
59
|
+
end
|
60
|
+
|
61
|
+
def install_importmap
|
62
|
+
say "Creating Turbo Mount initializer"
|
63
|
+
template "turbo-mount.js", File.join("app/javascript/turbo-mount-initializer.js")
|
64
|
+
append_to_file "app/javascript/application.js", %(import "turbo-mount-initializer"\n)
|
65
|
+
|
66
|
+
say "Pinning Turbo Mount to the importmap"
|
67
|
+
append_to_file "config/importmap.rb", %(pin "turbo-mount", to: "turbo-mount.min.js"\n)
|
68
|
+
append_to_file "config/importmap.rb", %(pin "turbo-mount/#{framework}", to: "turbo-mount/#{framework}.min.js"\n)
|
69
|
+
append_to_file "config/importmap.rb", %(pin "turbo-mount-initializer"\n)
|
70
|
+
|
71
|
+
say "Pinning framework dependencies to the importmap"
|
72
|
+
run "bin/importmap pin #{FRAMEWORKS[framework][:pins]}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def vite?
|
76
|
+
Dir.glob(Rails.root.join("vite.config.*")).any?
|
77
|
+
end
|
78
|
+
|
79
|
+
def importmap?
|
80
|
+
build_tool == "importmap"
|
81
|
+
end
|
82
|
+
|
83
|
+
def warn_about_vite_plugin
|
84
|
+
say "Make sure to install and add #{FRAMEWORKS[framework][:vite_plugin]} to your Vite config", :yellow
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_tool
|
88
|
+
return @build_tool if defined?(@build_tool)
|
89
|
+
|
90
|
+
@build_tool = detect_build_tool
|
91
|
+
end
|
92
|
+
|
93
|
+
def detect_build_tool
|
94
|
+
if Rails.root.join("package.json").exist?
|
95
|
+
if Rails.root.join("package-lock.json").exist?
|
96
|
+
"npm"
|
97
|
+
elsif Rails.root.join("bun.config.js").exist?
|
98
|
+
"bun"
|
99
|
+
else
|
100
|
+
"yarn"
|
101
|
+
end
|
102
|
+
elsif Rails.root.join("config/importmap.rb").exist?
|
103
|
+
"importmap"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def framework
|
108
|
+
@framework ||= ask("What framework do you want to use with Turbo Mount?", limited_to: FRAMEWORKS.keys, default: "react")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/turbo/mount/helpers.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
module Turbo
|
4
4
|
module Mount
|
5
5
|
module Helpers
|
6
|
-
def
|
6
|
+
def turbo_mount(component_name, props: {}, tag: "div", **attrs, &block)
|
7
7
|
raise TypeError, "Component name expected" unless component_name.is_a? String
|
8
8
|
|
9
|
-
controller_name = "turbo-mount-#{
|
9
|
+
controller_name = "turbo-mount-#{component_name.underscore.dasherize}"
|
10
10
|
attrs["data-controller"] = controller_name
|
11
11
|
prefix = "data-#{controller_name}"
|
12
12
|
attrs["#{prefix}-component-value"] = component_name
|
@@ -16,18 +16,7 @@ module Turbo
|
|
16
16
|
|
17
17
|
content_tag(tag, nil, attrs) { capture(controller_name, &block) }
|
18
18
|
end
|
19
|
-
|
20
|
-
def turbo_mount_react_component(component_name, **attrs, &block)
|
21
|
-
turbo_mount_component(component_name, framework: "react", **attrs, &block)
|
22
|
-
end
|
23
|
-
|
24
|
-
def turbo_mount_svelte_component(component_name, **attrs, &block)
|
25
|
-
turbo_mount_component(component_name, framework: "svelte", **attrs, &block)
|
26
|
-
end
|
27
|
-
|
28
|
-
def turbo_mount_vue_component(component_name, **attrs, &block)
|
29
|
-
turbo_mount_component(component_name, framework: "vue", **attrs, &block)
|
30
|
-
end
|
19
|
+
alias_method :turbo_mount_component, :turbo_mount
|
31
20
|
end
|
32
21
|
end
|
33
22
|
end
|
data/lib/turbo/mount/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-mount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -47,6 +47,8 @@ files:
|
|
47
47
|
- app/assets/javascripts/turbo-mount/vue.js
|
48
48
|
- app/assets/javascripts/turbo-mount/vue.min.js
|
49
49
|
- app/assets/javascripts/turbo-mount/vue.min.js.map
|
50
|
+
- lib/generators/turbo_mount/install/turbo-mount.js.tt
|
51
|
+
- lib/generators/turbo_mount/install_generator.rb
|
50
52
|
- lib/turbo/mount.rb
|
51
53
|
- lib/turbo/mount/engine.rb
|
52
54
|
- lib/turbo/mount/helpers.rb
|