turbo-mount 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -3
- data/README.md +144 -34
- data/app/assets/javascripts/turbo-mount/react.js +11 -15
- 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 +11 -15
- 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 +11 -15
- 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 +60 -23
- 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 +7 -15
- 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,30 @@ 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
|
+
|
25
|
+
## [0.2.3] - 2024-05-12
|
26
|
+
|
27
|
+
### Added
|
28
|
+
|
29
|
+
- Add a mount target to the base controller. ([@skryukov])
|
30
|
+
- Add `registerComponents` helper for vite. ([@skryukov])
|
31
|
+
- Allow to omit the `application` property in the constructor. ([@skryukov])
|
32
|
+
`TurboMount` will try to find the application in the `window.Stimulus` and will initialize new one if not found.
|
33
|
+
|
10
34
|
## [0.2.2] - 2024-05-09
|
11
35
|
|
12
36
|
### Fixed
|
@@ -27,9 +51,11 @@ and this project adheres to [Semantic Versioning].
|
|
27
51
|
|
28
52
|
[@skryukov]: https://github.com/skryukov
|
29
53
|
|
30
|
-
[Unreleased]: https://github.com/skryukov/turbo-mount/compare/v0.
|
31
|
-
[0.
|
32
|
-
[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
|
33
59
|
[0.1.0]: https://github.com/skryukov/turbo-mount/commits/v0.1.0
|
34
60
|
|
35
61
|
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
data/README.md
CHANGED
@@ -1,57 +1,143 @@
|
|
1
|
-
# Turbo
|
1
|
+
# Turbo Mount
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/turbo-mount.svg)](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
|
31
|
+
gem "turbo-mount"
|
32
|
+
```
|
33
|
+
|
34
|
+
### Automatic Installation
|
35
|
+
|
36
|
+
Run the following command to install the necessary files:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
bin/rails generate turbo_mount:install
|
40
|
+
```
|
41
|
+
|
42
|
+
This will add `turbo-mount` package and framework dependencies to your `package.json` or `importmap.rb`, and create the Turbo Mount initialization file.
|
43
|
+
|
44
|
+
### Manual Installation
|
45
|
+
|
46
|
+
You can also install the necessary JavaScript files manually.
|
47
|
+
|
48
|
+
If your project utilizes build tools such as [Vite](http://vite-ruby.netlify.app), also install the `turbo-mount` package:
|
49
|
+
|
50
|
+
```bash
|
51
|
+
npm install turbo-mount
|
52
|
+
# or with yarn
|
53
|
+
yarn add turbo-mount
|
12
54
|
|
13
|
-
|
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`.
|
64
|
+
|
65
|
+
### Importmaps
|
66
|
+
To use `TurboMount` with importmaps, you need to pin the necessary JavaScript files in your `config/importmap.rb`:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
pin "turbo-mount", to: "turbo-mount.min.js"
|
70
|
+
pin "turbo-mount/react", to: "turbo-mount/react.min.js"
|
71
|
+
```
|
72
|
+
|
73
|
+
This ensures that `turbo-mount` and its plugins are available in your application.
|
74
|
+
|
75
|
+
Also pin the desired framework:
|
76
|
+
|
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
|
+
```
|
14
84
|
|
15
|
-
|
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.
|
16
86
|
|
17
87
|
## Usage
|
18
88
|
|
19
|
-
|
89
|
+
### Initialization
|
90
|
+
|
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:
|
20
92
|
|
21
93
|
```js
|
22
|
-
import {
|
94
|
+
import { TurboMount } from "turbo-mount";
|
95
|
+
import { registerComponent } from "turbo-mount/react";
|
96
|
+
import { HexColorPicker } from 'react-colorful';
|
23
97
|
|
24
|
-
const
|
98
|
+
const turboMount = new TurboMount(); // or new TurboMount({ application })
|
25
99
|
|
26
|
-
|
27
|
-
|
100
|
+
registerComponent(turboMount, "HexColorPicker", HexColorPicker);
|
101
|
+
```
|
28
102
|
|
29
|
-
|
30
|
-
const turboMount = new TurboMount({application, plugin});
|
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.
|
31
104
|
|
32
|
-
|
33
|
-
import { SketchPicker } from 'react-color'
|
34
|
-
turboMount.register('SketchPicker', SketchPicker);
|
35
|
-
```
|
105
|
+
### View Helpers
|
36
106
|
|
37
|
-
|
107
|
+
Use the following helpers to mount components in your views:
|
38
108
|
|
39
109
|
```erb
|
40
|
-
<%=
|
110
|
+
<%= turbo_mount("HexColorPicker", props: {color: "#034"}, class: "mb-5") %>
|
111
|
+
```
|
41
112
|
|
42
|
-
|
113
|
+
This will generate the following HTML:
|
43
114
|
|
44
|
-
|
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>
|
45
121
|
```
|
46
122
|
|
47
|
-
|
123
|
+
### Supported Frameworks
|
48
124
|
|
49
|
-
|
50
|
-
// javascript/controllers/turbo_mount_react_sketch_picker_controller.js
|
125
|
+
`TurboMount` supports the following frameworks:
|
51
126
|
|
52
|
-
|
127
|
+
- React: `"turbo-mount/react"`
|
128
|
+
- Vue: `"turbo-mount/vue"`
|
129
|
+
- Svelte: `"turbo-mount/svelte"`
|
130
|
+
|
131
|
+
To add support for other frameworks, create a custom plugin. See included plugins for examples.
|
132
|
+
|
133
|
+
### Custom Controllers
|
134
|
+
|
135
|
+
To customize component behavior or pass functions as props, create a custom controller:
|
136
|
+
|
137
|
+
```js
|
138
|
+
import { TurboMountController } from "turbo-mount";
|
53
139
|
|
54
|
-
export default class extends
|
140
|
+
export default class extends TurboMountController {
|
55
141
|
get componentProps() {
|
56
142
|
return {
|
57
143
|
...this.propsValue,
|
@@ -60,26 +146,50 @@ export default class extends TurboMountReactController {
|
|
60
146
|
}
|
61
147
|
|
62
148
|
onChange = (color) => {
|
63
|
-
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 };
|
64
152
|
};
|
65
153
|
}
|
66
154
|
```
|
67
155
|
|
68
|
-
Then pass this controller the
|
156
|
+
Then pass this controller to the `registerComponent` method:
|
69
157
|
|
70
158
|
```js
|
71
|
-
|
159
|
+
import HexColorPickerController from "controllers/turbo_mount/hex_color_picker_controller";
|
160
|
+
|
161
|
+
registerComponent(turboMount, "HexColorPicker", HexColorPicker, HexColorPickerController);
|
72
162
|
```
|
73
163
|
|
74
|
-
|
164
|
+
### Vite Integration
|
165
|
+
|
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:
|
75
167
|
|
76
|
-
|
168
|
+
```js
|
169
|
+
import { TurboMount } from "turbo-mount/react";
|
170
|
+
import { registerComponents } from "turbo-mount/registerComponents/react";
|
77
171
|
|
78
|
-
|
172
|
+
const controllers = import.meta.glob("./**/*_controller.js", { eager: true });
|
173
|
+
const components = import.meta.glob("/components/**/*.jsx", { eager: true });
|
174
|
+
|
175
|
+
const turboMount = new TurboMount();
|
176
|
+
registerComponents({ turboMount, components, controllers });
|
177
|
+
```
|
79
178
|
|
80
|
-
|
179
|
+
The `registerComponents` helper searches for controllers in the following paths:
|
180
|
+
- `controllers/turbo-mount/${controllerName}`
|
181
|
+
- `controllers/turbo-mount-${controllerName}`
|
81
182
|
|
82
|
-
|
183
|
+
### Mount Target
|
184
|
+
|
185
|
+
To specify a non-root mount target, use the `data-<%= controller_name %>-target="mount"` attribute:
|
186
|
+
|
187
|
+
```erb
|
188
|
+
<%= turbo_mount("HexColorPicker", props: {color: "#430"}) do |controller_name| %>
|
189
|
+
<h3>Color picker</h3>
|
190
|
+
<div data-<%= controller_name %>-target="mount"></div>
|
191
|
+
<% end %>
|
192
|
+
```
|
83
193
|
|
84
194
|
## License
|
85
195
|
|
@@ -1,22 +1,18 @@
|
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
1
3
|
import { createElement } from 'react';
|
2
4
|
import { createRoot } from 'react-dom/client';
|
3
|
-
import { TurboMountController } from 'turbo-mount';
|
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
|
-
return () => {
|
14
|
-
|
15
|
-
}
|
16
|
-
|
17
|
-
const plugin = {
|
18
|
-
framework: "react",
|
19
|
-
controller: TurboMountReactController
|
11
|
+
return () => {
|
12
|
+
root.unmount();
|
13
|
+
};
|
14
|
+
},
|
20
15
|
};
|
16
|
+
const registerComponent = buildRegisterFunction(plugin);
|
21
17
|
|
22
|
-
export { plugin as default };
|
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,19 +1,15 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
class TurboMountSvelteController extends TurboMountController {
|
4
|
-
constructor() {
|
5
|
-
super(...arguments);
|
6
|
-
this.framework = "svelte";
|
7
|
-
}
|
8
|
-
mountComponent(el, Component, props) {
|
9
|
-
const component = new Component({ target: el, props });
|
10
|
-
return () => { component.$destroy(); };
|
11
|
-
}
|
12
|
-
}
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
13
3
|
|
14
4
|
const plugin = {
|
15
|
-
|
16
|
-
|
5
|
+
mountComponent: (mountProps) => {
|
6
|
+
const { el, Component, props } = mountProps;
|
7
|
+
const component = new Component({ target: el, props });
|
8
|
+
return () => {
|
9
|
+
component.$destroy();
|
10
|
+
};
|
11
|
+
},
|
17
12
|
};
|
13
|
+
const registerComponent = buildRegisterFunction(plugin);
|
18
14
|
|
19
|
-
export { plugin as default };
|
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,21 +1,17 @@
|
|
1
|
+
import { buildRegisterFunction } from 'turbo-mount';
|
2
|
+
export { TurboMount } from 'turbo-mount';
|
1
3
|
import { createApp } from 'vue';
|
2
|
-
import { TurboMountController } from 'turbo-mount';
|
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
|
-
return () => {
|
13
|
-
|
14
|
-
}
|
15
|
-
|
16
|
-
const plugin = {
|
17
|
-
framework: "vue",
|
18
|
-
controller: TurboMountVueController
|
10
|
+
return () => {
|
11
|
+
app.unmount();
|
12
|
+
};
|
13
|
+
},
|
19
14
|
};
|
15
|
+
const registerComponent = buildRegisterFunction(plugin);
|
20
16
|
|
21
|
-
export { plugin as default };
|
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
|
-
import { Controller } from '@hotwired/stimulus';
|
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
|
}
|
@@ -15,46 +23,65 @@ class TurboMountController extends Controller {
|
|
15
23
|
return this.propsValue;
|
16
24
|
}
|
17
25
|
get mountElement() {
|
18
|
-
return this.element;
|
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 = {
|
33
51
|
props: Object,
|
34
|
-
component: String
|
52
|
+
component: String,
|
53
|
+
};
|
54
|
+
TurboMountController.targets = ["mount"];
|
55
|
+
|
56
|
+
const camelToKebabCase = (str) => {
|
57
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
35
58
|
};
|
36
59
|
|
37
60
|
class TurboMount {
|
38
|
-
constructor(props) {
|
39
|
-
var _a;
|
61
|
+
constructor(props = {}) {
|
40
62
|
this.components = new Map();
|
41
|
-
this.application = props.application;
|
42
|
-
this.
|
43
|
-
this.
|
44
|
-
(
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
+
});
|
49
76
|
}
|
50
|
-
register(name, component, controller) {
|
51
|
-
controller || (controller =
|
77
|
+
register(plugin, name, component, controller) {
|
78
|
+
controller || (controller = TurboMountController);
|
52
79
|
if (this.components.has(name)) {
|
53
80
|
throw new Error(`Component '${name}' is already registered.`);
|
54
81
|
}
|
55
|
-
this.components.set(name, component);
|
82
|
+
this.components.set(name, { component, plugin });
|
56
83
|
if (controller) {
|
57
|
-
const controllerName = `turbo-mount-${
|
84
|
+
const controllerName = `turbo-mount-${camelToKebabCase(name)}`;
|
58
85
|
this.application.register(controllerName, controller);
|
59
86
|
}
|
60
87
|
}
|
@@ -65,9 +92,19 @@ class TurboMount {
|
|
65
92
|
}
|
66
93
|
return component;
|
67
94
|
}
|
68
|
-
|
69
|
-
|
95
|
+
findOrStartApplication(hydratedApp) {
|
96
|
+
let application = hydratedApp || window.Stimulus;
|
97
|
+
if (!application) {
|
98
|
+
application = Application.start();
|
99
|
+
window.Stimulus = application;
|
100
|
+
}
|
101
|
+
return application;
|
70
102
|
}
|
71
103
|
}
|
104
|
+
function buildRegisterFunction(plugin) {
|
105
|
+
return (turboMount, name, component, controller) => {
|
106
|
+
turboMount.register(plugin, name, component, controller);
|
107
|
+
};
|
108
|
+
}
|
72
109
|
|
73
|
-
export { TurboMount, TurboMountController };
|
110
|
+
export { TurboMount, TurboMountController, buildRegisterFunction };
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{Controller as t}from"@hotwired/stimulus";class
|
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"],"sourcesContent":["import {Controller} from \"@hotwired/stimulus\"
|
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,28 +3,20 @@
|
|
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
|
-
|
10
|
-
|
9
|
+
controller_name = "turbo-mount-#{component_name.underscore.dasherize}"
|
10
|
+
attrs["data-controller"] = controller_name
|
11
|
+
prefix = "data-#{controller_name}"
|
11
12
|
attrs["#{prefix}-component-value"] = component_name
|
12
13
|
attrs["#{prefix}-props-value"] = json_escape(props.to_json) if props.present?
|
13
14
|
|
14
|
-
content_tag(tag, nil, attrs)
|
15
|
-
end
|
16
|
-
|
17
|
-
def turbo_mount_react_component(component_name, **attrs)
|
18
|
-
turbo_mount_component(component_name, framework: "react", **attrs)
|
19
|
-
end
|
20
|
-
|
21
|
-
def turbo_mount_svelte_component(component_name, **attrs)
|
22
|
-
turbo_mount_component(component_name, framework: "svelte", **attrs)
|
23
|
-
end
|
15
|
+
return content_tag(tag, nil, attrs) unless block
|
24
16
|
|
25
|
-
|
26
|
-
turbo_mount_component(component_name, framework: "vue", **attrs)
|
17
|
+
content_tag(tag, nil, attrs) { capture(controller_name, &block) }
|
27
18
|
end
|
19
|
+
alias_method :turbo_mount_component, :turbo_mount
|
28
20
|
end
|
29
21
|
end
|
30
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
|