stimulus-components 1.0.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/app/javascript/animated-number.js +66 -0
- data/app/javascript/auto-submit.js +27 -0
- data/app/javascript/carousel.js +28 -0
- data/app/javascript/character-counter.js +47 -0
- data/app/javascript/chartjs.js +58 -0
- data/app/javascript/checkbox-select-all.js +76 -0
- data/app/javascript/clipboard.js +48 -0
- data/app/javascript/color-picker.js +75 -0
- data/app/javascript/confirmation.js +30 -0
- data/app/javascript/content-loader.js +109 -0
- data/app/javascript/dialog.js +53 -0
- data/app/javascript/dropdown.js +27 -0
- data/app/javascript/glow.js +30 -0
- data/app/javascript/hotkey.js +26 -0
- data/app/javascript/lightbox.js +26 -0
- data/app/javascript/notification.js +49 -0
- data/app/javascript/password-visibility.js +27 -0
- data/app/javascript/places-autocomplete.js +123 -0
- data/app/javascript/popover.js +53 -0
- data/app/javascript/prefetch.js +56 -0
- data/app/javascript/rails-nested-form.js +43 -0
- data/app/javascript/read-more.js +38 -0
- data/app/javascript/remote-rails.js +27 -0
- data/app/javascript/reveal-controller.js +33 -0
- data/app/javascript/scroll-progress.js +32 -0
- data/app/javascript/scroll-reveal.js +62 -0
- data/app/javascript/scroll-to.js +56 -0
- data/app/javascript/sortable.js +74 -0
- data/app/javascript/sound.js +39 -0
- data/app/javascript/speech-recognition.js +92 -0
- data/app/javascript/stimulus-components.js +71 -0
- data/app/javascript/textarea-autogrow.js +45 -0
- data/app/javascript/timeago.js +66 -0
- data/lib/stimulus-components/engine.rb +9 -0
- data/lib/stimulus-components/version.rb +3 -0
- data/lib/stimulus-components.rb +43 -0
- metadata +98 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a8c412b605bdc72f29a30d0d09516727ba0089c2dbf17aa0b530ed86499f0fd8
|
|
4
|
+
data.tar.gz: eb6ddc8d35cb27654bc6a2f8bb4308334f0e5a05aa3d659e3b2005da8e5159be
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 22160175d85e4a96474202ec29ddb5e640197299d102d18ca5ecc6e98bf6ce23380aae4f1f3377cea7e01829cd148ba73bb86076786d27d1fc31f78a1f6003ce
|
|
7
|
+
data.tar.gz: 6aa949ac5cf13530a7adb45faa95e1b3679f85936849fd32303be410a463218ddc1b9fb6f8ce51a9d762d4d01e752746a3148dcfc444cabca52fda000a69e326
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Stimulus Components
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Stimulus Components
|
|
2
|
+
|
|
3
|
+
A Ruby gem that packages the [stimulus-components](https://github.com/stimulus-components/stimulus-components) collection of 34+ Stimulus controllers for easy integration with Rails applications.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Rails 7.1+
|
|
8
|
+
- Ruby 3.1+
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Add this line to your application's Gemfile:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
gem "stimulus-components"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Importmap (Recommended for Rails 7.1+)
|
|
27
|
+
|
|
28
|
+
Add to your `config/importmap.rb`:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
pin "stimulus-components", to: "stimulus-components.js"
|
|
32
|
+
pin "@hotwired/stimulus", to: "https://cdn.jsdelivr.net/gh/hotwired/stimulus@3.2.2/dist/stimulus.umd.js"
|
|
33
|
+
pin "@hotwired/stimulus-loading", to: "https://cdn.jsdelivr.net/gh/hotwired/stimulus-loading@1.0.5/dist/stimulus-loading.umd.js"
|
|
34
|
+
pin "stimulus-use", to: "https://cdn.jsdelivr.net/npm/stimulus-use@1.0.0/dist/index.js"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Sprockets
|
|
38
|
+
|
|
39
|
+
Add to your `app/assets/javascripts/application.js`:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
//= require stimulus-components
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Application Controller
|
|
46
|
+
|
|
47
|
+
In your JavaScript entry point (e.g., `app/javascript/controllers/index.js`):
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
import { application } from "stimulus-components"
|
|
51
|
+
|
|
52
|
+
window.Stimulus = application
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or import individual controllers:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import ClipboardController from "stimulus-components/clipboard"
|
|
59
|
+
import PasswordVisibilityController from "stimulus-components/password-visibility"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Available Controllers
|
|
63
|
+
|
|
64
|
+
| Controller | Description |
|
|
65
|
+
|------------|-------------|
|
|
66
|
+
| `animated-number` | Animates numbers on scroll |
|
|
67
|
+
| `auto-submit` | Auto-submits forms with debounce |
|
|
68
|
+
| `carousel` | Swiper-based carousel |
|
|
69
|
+
| `character-counter` | Counts characters in inputs |
|
|
70
|
+
| `chartjs` | Chart.js integration |
|
|
71
|
+
| `checkbox-select-all` | Select all checkbox functionality |
|
|
72
|
+
| `clipboard` | Copy to clipboard |
|
|
73
|
+
| `color-picker` | Color picker with Pickr |
|
|
74
|
+
| `confirmation` | Form confirmation checks |
|
|
75
|
+
| `content-loader` | Load remote content |
|
|
76
|
+
| `dialog` | Native dialog element |
|
|
77
|
+
| `dropdown` | Dropdown with transitions |
|
|
78
|
+
| `glow` | Hover glow effect |
|
|
79
|
+
| `hotkey` | Keyboard shortcuts |
|
|
80
|
+
| `lightbox` | Image lightbox gallery |
|
|
81
|
+
| `notification` | Toast notifications |
|
|
82
|
+
| `password-visibility` | Toggle password visibility |
|
|
83
|
+
| `places-autocomplete` | Google Places autocomplete |
|
|
84
|
+
| `popover` | Popover content display |
|
|
85
|
+
| `prefetch` | Link prefetching |
|
|
86
|
+
| `rails-nested-form` | Nested form management |
|
|
87
|
+
| `read-more` | Expandable content |
|
|
88
|
+
| `remote-rails` | Remote response handling |
|
|
89
|
+
| `reveal` | Toggle element visibility |
|
|
90
|
+
| `scroll-progress` | Scroll progress bar |
|
|
91
|
+
| `scroll-reveal` | Scroll-based animations |
|
|
92
|
+
| `scroll-to` | Smooth scroll to element |
|
|
93
|
+
| `sortable` | Sortable lists |
|
|
94
|
+
| `sound` | Audio playback |
|
|
95
|
+
| `speech-recognition` | Voice input |
|
|
96
|
+
| `textarea-autogrow` | Auto-growing textarea |
|
|
97
|
+
| `timeago` | Relative timestamps |
|
|
98
|
+
|
|
99
|
+
## External Dependencies
|
|
100
|
+
|
|
101
|
+
Some controllers require external libraries. Add these to your `package.json` or via CDN:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"dependencies": {
|
|
106
|
+
"@hotwired/stimulus": "^3.2",
|
|
107
|
+
"@rails/request.js": "^0.0.8",
|
|
108
|
+
"chart.js": "^4.0",
|
|
109
|
+
"lightgallery": "^2.0",
|
|
110
|
+
"sortablejs": "^1.15",
|
|
111
|
+
"stimulus-use": "^1.0",
|
|
112
|
+
"swiper": "^11.0",
|
|
113
|
+
"@simonwep/pickr": "^1.9",
|
|
114
|
+
"date-fns": "^3.0"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Documentation
|
|
120
|
+
|
|
121
|
+
For detailed usage instructions, see the [official documentation](https://www.stimulus-components.com/).
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT License - see LICENSE.txt
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
1. Fork it
|
|
130
|
+
2. Create your feature branch
|
|
131
|
+
3. Commit your changes
|
|
132
|
+
4. Push to the branch
|
|
133
|
+
5. Create a new Pull Request
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class AnimatedNumber extends Controller<HTMLElement> {
|
|
4
|
+
declare lazyThresholdValue: number
|
|
5
|
+
declare lazyRootMarginValue: string
|
|
6
|
+
declare startValue: number
|
|
7
|
+
declare endValue: number
|
|
8
|
+
declare durationValue: number
|
|
9
|
+
declare lazyValue: number
|
|
10
|
+
|
|
11
|
+
static values = {
|
|
12
|
+
start: Number,
|
|
13
|
+
end: Number,
|
|
14
|
+
duration: Number,
|
|
15
|
+
lazyThreshold: Number,
|
|
16
|
+
lazyRootMargin: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: "0px",
|
|
19
|
+
},
|
|
20
|
+
lazy: Boolean,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
connect(): void {
|
|
24
|
+
this.lazyValue ? this.lazyAnimate() : this.animate()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
animate(): void {
|
|
28
|
+
let startTimestamp: number = null
|
|
29
|
+
|
|
30
|
+
const step = (timestamp: number) => {
|
|
31
|
+
if (!startTimestamp) startTimestamp = timestamp
|
|
32
|
+
|
|
33
|
+
const elapsed: number = timestamp - startTimestamp
|
|
34
|
+
const progress: number = Math.min(elapsed / this.durationValue, 1)
|
|
35
|
+
|
|
36
|
+
this.element.innerHTML = Math.floor(progress * (this.endValue - this.startValue) + this.startValue).toString()
|
|
37
|
+
|
|
38
|
+
if (progress < 1) {
|
|
39
|
+
window.requestAnimationFrame(step)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
window.requestAnimationFrame(step)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
lazyAnimate(): void {
|
|
47
|
+
const observer = new IntersectionObserver((entries, observer) => {
|
|
48
|
+
entries.forEach((entry: IntersectionObserverEntry) => {
|
|
49
|
+
if (entry.isIntersecting) {
|
|
50
|
+
this.animate()
|
|
51
|
+
|
|
52
|
+
observer.unobserve(entry.target)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}, this.lazyAnimateOptions)
|
|
56
|
+
|
|
57
|
+
observer.observe(this.element)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get lazyAnimateOptions(): IntersectionObserverInit {
|
|
61
|
+
return {
|
|
62
|
+
threshold: this.lazyThresholdValue,
|
|
63
|
+
rootMargin: this.lazyRootMarginValue,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { debounce } from "stimulus-use"
|
|
3
|
+
|
|
4
|
+
export default class AutoSubmit extends Controller<HTMLFormElement> {
|
|
5
|
+
declare delayValue: number
|
|
6
|
+
|
|
7
|
+
static values = {
|
|
8
|
+
delay: {
|
|
9
|
+
type: Number,
|
|
10
|
+
default: 150,
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
initialize(): void {
|
|
15
|
+
this.submit = this.submit.bind(this)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
connect(): void {
|
|
19
|
+
if (this.delayValue > 0) {
|
|
20
|
+
this.submit = debounce(this.submit, this.delayValue)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
submit(): void {
|
|
25
|
+
this.element.requestSubmit()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import SwiperOptions from "swiper"
|
|
3
|
+
import Swiper from "swiper/bundle"
|
|
4
|
+
|
|
5
|
+
export default class Carousel extends Controller {
|
|
6
|
+
declare swiper: Swiper
|
|
7
|
+
declare optionsValue: SwiperOptions
|
|
8
|
+
|
|
9
|
+
static values = {
|
|
10
|
+
options: Object,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
connect(): void {
|
|
14
|
+
this.swiper = new Swiper(this.element, {
|
|
15
|
+
...this.defaultOptions,
|
|
16
|
+
...this.optionsValue,
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
disconnect(): void {
|
|
21
|
+
this.swiper.destroy()
|
|
22
|
+
this.swiper = undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get defaultOptions(): SwiperOptions {
|
|
26
|
+
return {}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class CharacterCounter extends Controller {
|
|
4
|
+
declare readonly counterTarget: HTMLElement
|
|
5
|
+
declare readonly inputTarget: HTMLInputElement
|
|
6
|
+
declare readonly hasCountdownValue: boolean
|
|
7
|
+
|
|
8
|
+
static targets = ["input", "counter"]
|
|
9
|
+
static values = { countdown: Boolean }
|
|
10
|
+
|
|
11
|
+
initialize(): void {
|
|
12
|
+
this.update = this.update.bind(this)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connect(): void {
|
|
16
|
+
this.update()
|
|
17
|
+
this.inputTarget.addEventListener("input", this.update)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
disconnect(): void {
|
|
21
|
+
this.inputTarget.removeEventListener("input", this.update)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
update(): void {
|
|
25
|
+
this.counterTarget.innerHTML = this.count.toLocaleString()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get count(): number {
|
|
29
|
+
let value: number = this.inputTarget.value.length
|
|
30
|
+
|
|
31
|
+
if (this.hasCountdownValue) {
|
|
32
|
+
if (this.maxLength < 0) {
|
|
33
|
+
console.error(
|
|
34
|
+
`[stimulus-character-counter] You need to add a maxlength attribute on the input to use countdown mode.`
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
value = Math.max(this.maxLength - value, 0)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get maxLength(): number {
|
|
45
|
+
return this.inputTarget.maxLength
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { Chart, registerables, ChartType, ChartOptions, ChartData } from "chart.js"
|
|
3
|
+
|
|
4
|
+
Chart.register(...registerables)
|
|
5
|
+
|
|
6
|
+
export default class Chartjs extends Controller<HTMLCanvasElement> {
|
|
7
|
+
declare canvasTarget: HTMLCanvasElement
|
|
8
|
+
declare chart: Chart
|
|
9
|
+
declare typeValue: ChartType
|
|
10
|
+
declare optionsValue: ChartOptions
|
|
11
|
+
declare dataValue: ChartData
|
|
12
|
+
declare hasDataValue: boolean
|
|
13
|
+
declare hasCanvasTarget: boolean
|
|
14
|
+
|
|
15
|
+
static targets = ["canvas"]
|
|
16
|
+
static values = {
|
|
17
|
+
type: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: "line",
|
|
20
|
+
},
|
|
21
|
+
data: Object,
|
|
22
|
+
options: Object,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
connect(): void {
|
|
26
|
+
const element = this.hasCanvasTarget ? this.canvasTarget : this.element
|
|
27
|
+
|
|
28
|
+
this.chart = new Chart(element.getContext("2d"), {
|
|
29
|
+
type: this.typeValue,
|
|
30
|
+
data: this.chartData,
|
|
31
|
+
options: this.chartOptions,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
disconnect(): void {
|
|
36
|
+
this.chart.destroy()
|
|
37
|
+
this.chart = undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get chartData(): ChartData {
|
|
41
|
+
if (!this.hasDataValue) {
|
|
42
|
+
console.warn("[@stimulus-components/chartjs] You need to pass data as JSON to see the chart.")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return this.dataValue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get chartOptions(): ChartOptions {
|
|
49
|
+
return {
|
|
50
|
+
...this.defaultOptions,
|
|
51
|
+
...this.optionsValue,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get defaultOptions(): ChartOptions {
|
|
56
|
+
return {}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class CheckboxSelectAll extends Controller {
|
|
4
|
+
declare readonly hasCheckboxAllTarget: boolean
|
|
5
|
+
declare readonly checkboxTargets: HTMLInputElement[]
|
|
6
|
+
declare readonly checkboxAllTarget: HTMLInputElement
|
|
7
|
+
declare readonly disableIndeterminateValue: boolean
|
|
8
|
+
|
|
9
|
+
static targets = ["checkboxAll", "checkbox"]
|
|
10
|
+
|
|
11
|
+
static values = {
|
|
12
|
+
disableIndeterminate: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
initialize() {
|
|
19
|
+
this.toggle = this.toggle.bind(this)
|
|
20
|
+
this.refresh = this.refresh.bind(this)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
checkboxAllTargetConnected(checkbox: HTMLInputElement): void {
|
|
24
|
+
checkbox.addEventListener("change", this.toggle)
|
|
25
|
+
this.refresh()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
checkboxTargetConnected(checkbox: HTMLInputElement): void {
|
|
29
|
+
checkbox.addEventListener("change", this.refresh)
|
|
30
|
+
this.refresh()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
checkboxAllTargetDisconnected(checkbox: HTMLInputElement): void {
|
|
34
|
+
checkbox.removeEventListener("change", this.toggle)
|
|
35
|
+
this.refresh()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
checkboxTargetDisconnected(checkbox: HTMLInputElement): void {
|
|
39
|
+
checkbox.removeEventListener("change", this.refresh)
|
|
40
|
+
this.refresh()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toggle(e: Event): void {
|
|
44
|
+
e.preventDefault()
|
|
45
|
+
|
|
46
|
+
this.checkboxTargets.forEach((checkbox) => {
|
|
47
|
+
checkbox.checked = e.target.checked
|
|
48
|
+
this.triggerInputEvent(checkbox)
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
refresh(): void {
|
|
53
|
+
const checkboxesCount = this.checkboxTargets.length
|
|
54
|
+
const checkboxesCheckedCount = this.checked.length
|
|
55
|
+
|
|
56
|
+
if (this.disableIndeterminateValue) {
|
|
57
|
+
this.checkboxAllTarget.checked = checkboxesCheckedCount === checkboxesCount
|
|
58
|
+
} else {
|
|
59
|
+
this.checkboxAllTarget.checked = checkboxesCheckedCount > 0
|
|
60
|
+
this.checkboxAllTarget.indeterminate = checkboxesCheckedCount > 0 && checkboxesCheckedCount < checkboxesCount
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
triggerInputEvent(checkbox: HTMLInputElement): void {
|
|
65
|
+
const event = new Event("input", { bubbles: false, cancelable: true })
|
|
66
|
+
checkbox.dispatchEvent(event)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get checked(): HTMLInputElement[] {
|
|
70
|
+
return this.checkboxTargets.filter((checkbox) => checkbox.checked)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get unchecked(): HTMLInputElement[] {
|
|
74
|
+
return this.checkboxTargets.filter((checkbox) => !checkbox.checked)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class Clipboard extends Controller {
|
|
4
|
+
declare readonly hasButtonTarget: boolean
|
|
5
|
+
declare originalContent: string
|
|
6
|
+
declare successDurationValue: number
|
|
7
|
+
declare successContentValue: string
|
|
8
|
+
declare timeout: number
|
|
9
|
+
declare readonly buttonTarget: HTMLElement
|
|
10
|
+
declare readonly sourceTarget: HTMLInputElement
|
|
11
|
+
|
|
12
|
+
static targets = ["button", "source"]
|
|
13
|
+
static values = {
|
|
14
|
+
successContent: String,
|
|
15
|
+
successDuration: {
|
|
16
|
+
type: Number,
|
|
17
|
+
default: 2000,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
connect(): void {
|
|
22
|
+
if (!this.hasButtonTarget) return
|
|
23
|
+
|
|
24
|
+
this.originalContent = this.buttonTarget.innerHTML
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
copy(event: Event): void {
|
|
28
|
+
event.preventDefault()
|
|
29
|
+
|
|
30
|
+
const text = this.sourceTarget.innerHTML || this.sourceTarget.value
|
|
31
|
+
|
|
32
|
+
navigator.clipboard.writeText(text).then(() => this.copied())
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
copied(): void {
|
|
36
|
+
if (!this.hasButtonTarget) return
|
|
37
|
+
|
|
38
|
+
if (this.timeout) {
|
|
39
|
+
clearTimeout(this.timeout)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.buttonTarget.innerHTML = this.successContentValue
|
|
43
|
+
|
|
44
|
+
this.timeout = setTimeout(() => {
|
|
45
|
+
this.buttonTarget.innerHTML = this.originalContent
|
|
46
|
+
}, this.successDurationValue)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import Pickr from "@simonwep/pickr"
|
|
3
|
+
|
|
4
|
+
export default class ColorPicker extends Controller<HTMLElement> {
|
|
5
|
+
declare inputTarget: HTMLInputElement
|
|
6
|
+
declare buttonTarget: HTMLButtonElement
|
|
7
|
+
declare themeValue: Pickr.Theme
|
|
8
|
+
declare picker: Pickr
|
|
9
|
+
|
|
10
|
+
static targets = ["button", "input"]
|
|
11
|
+
|
|
12
|
+
static values = {
|
|
13
|
+
theme: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: "classic",
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
initialize() {
|
|
20
|
+
this.onSave = this.onSave.bind(this)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
connect() {
|
|
24
|
+
this.picker = Pickr.create({
|
|
25
|
+
el: this.buttonTarget,
|
|
26
|
+
theme: this.themeValue,
|
|
27
|
+
default: this.inputTarget.value,
|
|
28
|
+
swatches: this.swatches,
|
|
29
|
+
components: this.componentOptions,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
this.picker.on("save", this.onSave)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
disconnect() {
|
|
36
|
+
this.picker.destroy()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onSave(color: Pickr.HSVaColor) {
|
|
40
|
+
this.inputTarget.value = null
|
|
41
|
+
|
|
42
|
+
if (color) {
|
|
43
|
+
this.inputTarget.value = color.toHEXA().toString()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.picker.hide()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get componentOptions(): object {
|
|
50
|
+
return {
|
|
51
|
+
preview: true,
|
|
52
|
+
hue: true,
|
|
53
|
+
interaction: {
|
|
54
|
+
input: true,
|
|
55
|
+
clear: true,
|
|
56
|
+
save: true,
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get swatches(): string[] {
|
|
62
|
+
return [
|
|
63
|
+
"#A0AEC0",
|
|
64
|
+
"#F56565",
|
|
65
|
+
"#ED8936",
|
|
66
|
+
"#ECC94B",
|
|
67
|
+
"#48BB78",
|
|
68
|
+
"#38B2AC",
|
|
69
|
+
"#4299E1",
|
|
70
|
+
"#667EEA",
|
|
71
|
+
"#9F7AEA",
|
|
72
|
+
"#ED64A6",
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class Confirmation extends Controller<HTMLFormElement> {
|
|
4
|
+
declare inputTargets: HTMLInputElement[]
|
|
5
|
+
declare itemTargets: HTMLInputElement[] | HTMLButtonElement[]
|
|
6
|
+
|
|
7
|
+
static targets = ["input", "item"]
|
|
8
|
+
|
|
9
|
+
check(): void {
|
|
10
|
+
const disabled = this.inputTargets.some((input) => {
|
|
11
|
+
if (input.type === "checkbox") {
|
|
12
|
+
return input.checked === false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return input.dataset.confirmationContent !== input.value
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
this.itemTargets.forEach((target) => {
|
|
19
|
+
target.disabled = disabled
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
inputTargetConnected(): void {
|
|
24
|
+
this.check()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
inputTargetDisconnected(): void {
|
|
28
|
+
this.check()
|
|
29
|
+
}
|
|
30
|
+
}
|