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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class Reveal extends Controller {
|
|
4
|
+
declare hasHiddenClass: boolean
|
|
5
|
+
declare hiddenClass: string
|
|
6
|
+
declare itemTargets: HTMLElement[]
|
|
7
|
+
declare class: string
|
|
8
|
+
|
|
9
|
+
static targets = ["item"]
|
|
10
|
+
static classes = ["hidden"]
|
|
11
|
+
|
|
12
|
+
connect(): void {
|
|
13
|
+
this.class = this.hasHiddenClass ? this.hiddenClass : "hidden"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
toggle(): void {
|
|
17
|
+
this.itemTargets.forEach((item) => {
|
|
18
|
+
item.classList.toggle(this.class)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
show(): void {
|
|
23
|
+
this.itemTargets.forEach((item) => {
|
|
24
|
+
item.classList.remove(this.class)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
hide(): void {
|
|
29
|
+
this.itemTargets.forEach((item) => {
|
|
30
|
+
item.classList.add(this.class)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class ScrollProgress extends Controller<HTMLElement> {
|
|
4
|
+
declare readonly throttleDelayValue: number
|
|
5
|
+
|
|
6
|
+
static values = {
|
|
7
|
+
throttleDelay: {
|
|
8
|
+
type: Number,
|
|
9
|
+
default: 15,
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
initialize(): void {
|
|
14
|
+
this.scroll = this.scroll.bind(this)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
connect(): void {
|
|
18
|
+
window.addEventListener("scroll", this.scroll, { passive: true })
|
|
19
|
+
this.scroll()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
disconnect(): void {
|
|
23
|
+
window.removeEventListener("scroll", this.scroll)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
scroll(): void {
|
|
27
|
+
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight
|
|
28
|
+
const width = (window.scrollY / height) * 100
|
|
29
|
+
|
|
30
|
+
this.element.style.width = `${width}%`
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class ScrollReveal extends Controller {
|
|
4
|
+
declare classValue: string
|
|
5
|
+
declare thresholdValue: number
|
|
6
|
+
declare rootMarginValue: string
|
|
7
|
+
declare class: string
|
|
8
|
+
declare threshold: number
|
|
9
|
+
declare rootMargin: string
|
|
10
|
+
declare observer: IntersectionObserver
|
|
11
|
+
declare readonly itemTargets: HTMLElement[]
|
|
12
|
+
|
|
13
|
+
static targets = ["item"]
|
|
14
|
+
static values = {
|
|
15
|
+
class: String,
|
|
16
|
+
threshold: Number,
|
|
17
|
+
rootMargin: String,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
initialize(): void {
|
|
21
|
+
this.intersectionObserverCallback = this.intersectionObserverCallback.bind(this)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
connect(): void {
|
|
25
|
+
this.class = this.classValue || this.defaultOptions.class || "in"
|
|
26
|
+
this.threshold = this.thresholdValue || this.defaultOptions.threshold || 0.1
|
|
27
|
+
this.rootMargin = this.rootMarginValue || this.defaultOptions.rootMargin || "0px"
|
|
28
|
+
|
|
29
|
+
this.observer = new IntersectionObserver(this.intersectionObserverCallback, this.intersectionObserverOptions)
|
|
30
|
+
this.itemTargets.forEach((item) => this.observer.observe(item))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
disconnect(): void {
|
|
34
|
+
this.itemTargets.forEach((item) => this.observer.unobserve(item))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
intersectionObserverCallback(entries: IntersectionObserverEntry[], observer: IntersectionObserver): void {
|
|
38
|
+
entries.forEach((entry) => {
|
|
39
|
+
if (entry.intersectionRatio > this.threshold) {
|
|
40
|
+
const target = entry.target as HTMLElement
|
|
41
|
+
target.classList.add(...this.class.split(" "))
|
|
42
|
+
|
|
43
|
+
if (target.dataset.delay) {
|
|
44
|
+
target.style.transitionDelay = target.dataset.delay
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
observer.unobserve(target)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get intersectionObserverOptions(): IntersectionObserverInit {
|
|
53
|
+
return {
|
|
54
|
+
threshold: this.threshold,
|
|
55
|
+
rootMargin: this.rootMargin,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get defaultOptions(): { class?: string; threshold?: number; rootMargin?: string } {
|
|
60
|
+
return {}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class ScrollTo extends Controller<HTMLAnchorElement> {
|
|
4
|
+
declare offsetValue: number
|
|
5
|
+
declare behaviorValue: ScrollBehavior
|
|
6
|
+
declare hasOffsetValue: boolean
|
|
7
|
+
|
|
8
|
+
static values = {
|
|
9
|
+
offset: Number,
|
|
10
|
+
behavior: String,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
initialize(): void {
|
|
14
|
+
this.scroll = this.scroll.bind(this)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
connect(): void {
|
|
18
|
+
this.element.addEventListener("click", this.scroll)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
disconnect(): void {
|
|
22
|
+
this.element.removeEventListener("click", this.scroll)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
scroll(event: Event): void {
|
|
26
|
+
event.preventDefault()
|
|
27
|
+
|
|
28
|
+
const id = this.element.hash.replace(/^#/, "")
|
|
29
|
+
const target = document.getElementById(id)
|
|
30
|
+
|
|
31
|
+
if (!target) {
|
|
32
|
+
console.warn(`[@stimulus-components/scroll-to] The element with the id: "${id}" does not exist on the page.`)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const elementPosition = target.getBoundingClientRect().top + window.pageYOffset
|
|
37
|
+
const offsetPosition = elementPosition - this.offset
|
|
38
|
+
|
|
39
|
+
window.scrollTo({
|
|
40
|
+
top: offsetPosition,
|
|
41
|
+
behavior: this.behavior,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get offset(): number {
|
|
46
|
+
if (this.hasOffsetValue) {
|
|
47
|
+
return this.offsetValue
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return 10
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get behavior(): ScrollBehavior {
|
|
54
|
+
return this.behaviorValue || "smooth"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import Sortable from "sortablejs"
|
|
3
|
+
import { FetchRequest, type ResponseKind } from "@rails/request.js"
|
|
4
|
+
|
|
5
|
+
export default class Sortable extends Controller<HTMLElement> {
|
|
6
|
+
declare animationValue: number
|
|
7
|
+
declare resourceNameValue: string
|
|
8
|
+
declare paramNameValue: string
|
|
9
|
+
declare responseKindValue: ResponseKind
|
|
10
|
+
declare sortable: Sortable
|
|
11
|
+
declare handleValue: string
|
|
12
|
+
declare methodValue: string
|
|
13
|
+
|
|
14
|
+
static values = {
|
|
15
|
+
resourceName: String,
|
|
16
|
+
paramName: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: "position",
|
|
19
|
+
},
|
|
20
|
+
responseKind: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: "html",
|
|
23
|
+
},
|
|
24
|
+
animation: Number,
|
|
25
|
+
handle: String,
|
|
26
|
+
method: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: "patch",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
initialize() {
|
|
33
|
+
this.onUpdate = this.onUpdate.bind(this)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
connect() {
|
|
37
|
+
this.sortable = new Sortable(this.element, {
|
|
38
|
+
...this.defaultOptions,
|
|
39
|
+
...this.options,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
disconnect() {
|
|
44
|
+
this.sortable.destroy()
|
|
45
|
+
this.sortable = undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async onUpdate({ item, newIndex }) {
|
|
49
|
+
if (!item.dataset.sortableUpdateUrl) return
|
|
50
|
+
|
|
51
|
+
const param = this.resourceNameValue ? `${this.resourceNameValue}[${this.paramNameValue}]` : this.paramNameValue
|
|
52
|
+
|
|
53
|
+
const data = new FormData()
|
|
54
|
+
data.append(param, newIndex + 1)
|
|
55
|
+
|
|
56
|
+
const request = new FetchRequest(this.methodValue, item.dataset.sortableUpdateUrl, {
|
|
57
|
+
body: data,
|
|
58
|
+
responseKind: this.responseKindValue,
|
|
59
|
+
})
|
|
60
|
+
return await request.perform()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get options(): Sortable.Options {
|
|
64
|
+
return {
|
|
65
|
+
animation: this.animationValue || this.defaultOptions.animation || 150,
|
|
66
|
+
handle: this.handleValue || this.defaultOptions.handle || undefined,
|
|
67
|
+
onUpdate: this.onUpdate,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get defaultOptions(): Sortable.Options {
|
|
72
|
+
return {}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class Sound extends Controller {
|
|
4
|
+
declare readonly urlValue: string
|
|
5
|
+
declare sound: HTMLAudioElement
|
|
6
|
+
|
|
7
|
+
static values = {
|
|
8
|
+
url: String,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
connect() {
|
|
12
|
+
this.sound = new Audio(this.urlValue)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
play() {
|
|
16
|
+
this.sound.play()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pause() {
|
|
20
|
+
this.sound.pause()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
reset() {
|
|
24
|
+
this.sound.pause()
|
|
25
|
+
this.sound.load()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
volume({ params }) {
|
|
29
|
+
this.sound.volume = params.volume
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
muted({ params }) {
|
|
33
|
+
this.sound.muted = params.muted
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
loop({ params }) {
|
|
37
|
+
this.sound.loop = params.loop
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class SpeechRecognitionController extends Controller {
|
|
4
|
+
declare readonly startButtonTarget: HTMLButtonElement
|
|
5
|
+
declare readonly stopButtonTarget: HTMLButtonElement
|
|
6
|
+
declare readonly inputTarget: HTMLTextAreaElement | HTMLInputElement
|
|
7
|
+
declare readonly indicatorTarget: HTMLElement
|
|
8
|
+
declare readonly hasIndicatorTarget: boolean
|
|
9
|
+
declare readonly hiddenClass: string
|
|
10
|
+
declare readonly hasHiddenClass: boolean
|
|
11
|
+
|
|
12
|
+
static targets = ["startButton", "stopButton", "indicator", "input"]
|
|
13
|
+
static classes = ["hidden"]
|
|
14
|
+
|
|
15
|
+
private recognition: SpeechRecognition | null = null
|
|
16
|
+
private isListening = false
|
|
17
|
+
private hiddenClassName!: string
|
|
18
|
+
|
|
19
|
+
connect(): void {
|
|
20
|
+
this.hiddenClassName = this.hasHiddenClass ? this.hiddenClass : "hidden"
|
|
21
|
+
|
|
22
|
+
if (!this.isSupported) {
|
|
23
|
+
this.startButtonTarget.classList.add(this.hiddenClassName)
|
|
24
|
+
this.stopButtonTarget.classList.add(this.hiddenClassName)
|
|
25
|
+
if (this.hasIndicatorTarget) this.indicatorTarget.classList.add(this.hiddenClassName)
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.setupRecognition()
|
|
30
|
+
this.updateUI()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
disconnect(): void {
|
|
34
|
+
this.recognition?.abort()
|
|
35
|
+
this.recognition = null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
start(): void {
|
|
39
|
+
if (!this.recognition || this.isListening) return
|
|
40
|
+
|
|
41
|
+
this.recognition.start()
|
|
42
|
+
this.isListening = true
|
|
43
|
+
this.updateUI()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
stop(): void {
|
|
47
|
+
if (!this.recognition || !this.isListening) return
|
|
48
|
+
|
|
49
|
+
this.recognition.stop()
|
|
50
|
+
this.isListening = false
|
|
51
|
+
this.updateUI()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private get isSupported(): boolean {
|
|
55
|
+
return "SpeechRecognition" in window || "webkitSpeechRecognition" in window
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private setupRecognition(): void {
|
|
59
|
+
const SpeechRecognitionAPI = window.SpeechRecognition ?? window.webkitSpeechRecognition
|
|
60
|
+
this.recognition = new SpeechRecognitionAPI()
|
|
61
|
+
this.recognition.continuous = true
|
|
62
|
+
this.recognition.interimResults = true
|
|
63
|
+
|
|
64
|
+
this.recognition.onresult = (event): void => {
|
|
65
|
+
this.inputTarget.value = Array.from(event.results)
|
|
66
|
+
.map((result) => result[0].transcript)
|
|
67
|
+
.join("")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.recognition.onend = (): void => {
|
|
71
|
+
if (this.isListening) {
|
|
72
|
+
this.isListening = false
|
|
73
|
+
this.updateUI()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.recognition.onerror = (event): void => {
|
|
78
|
+
console.error("Speech recognition error:", event.error)
|
|
79
|
+
this.isListening = false
|
|
80
|
+
this.updateUI()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private updateUI(): void {
|
|
85
|
+
this.startButtonTarget.classList.toggle(this.hiddenClassName, this.isListening)
|
|
86
|
+
this.stopButtonTarget.classList.toggle(this.hiddenClassName, !this.isListening)
|
|
87
|
+
|
|
88
|
+
if (this.hasIndicatorTarget) {
|
|
89
|
+
this.indicatorTarget.classList.toggle(this.hiddenClassName, !this.isListening)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Application } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
import AnimatedNumberController from "./stimulus-components/animated-number"
|
|
4
|
+
import AutoSubmitController from "./stimulus-components/auto-submit"
|
|
5
|
+
import CarouselController from "./stimulus-components/carousel"
|
|
6
|
+
import CharacterCounterController from "./stimulus-components/character-counter"
|
|
7
|
+
import ChartjsController from "./stimulus-components/chartjs"
|
|
8
|
+
import CheckboxSelectAllController from "./stimulus-components/checkbox-select-all"
|
|
9
|
+
import ClipboardController from "./stimulus-components/clipboard"
|
|
10
|
+
import ColorPickerController from "./stimulus-components/color-picker"
|
|
11
|
+
import ConfirmationController from "./stimulus-components/confirmation"
|
|
12
|
+
import ContentLoaderController from "./stimulus-components/content-loader"
|
|
13
|
+
import DialogController from "./stimulus-components/dialog"
|
|
14
|
+
import DropdownController from "./stimulus-components/dropdown"
|
|
15
|
+
import GlowController from "./stimulus-components/glow"
|
|
16
|
+
import HotkeyController from "./stimulus-components/hotkey"
|
|
17
|
+
import LightboxController from "./stimulus-components/lightbox"
|
|
18
|
+
import NotificationController from "./stimulus-components/notification"
|
|
19
|
+
import PasswordVisibilityController from "./stimulus-components/password-visibility"
|
|
20
|
+
import PlacesAutocompleteController from "./stimulus-components/places-autocomplete"
|
|
21
|
+
import PopoverController from "./stimulus-components/popover"
|
|
22
|
+
import PrefetchController from "./stimulus-components/prefetch"
|
|
23
|
+
import RailsNestedFormController from "./stimulus-components/rails-nested-form"
|
|
24
|
+
import ReadMoreController from "./stimulus-components/read-more"
|
|
25
|
+
import RemoteRailsController from "./stimulus-components/remote-rails"
|
|
26
|
+
import RevealController from "./stimulus-components/reveal-controller"
|
|
27
|
+
import ScrollProgressController from "./stimulus-components/scroll-progress"
|
|
28
|
+
import ScrollRevealController from "./stimulus-components/scroll-reveal"
|
|
29
|
+
import ScrollToController from "./stimulus-components/scroll-to"
|
|
30
|
+
import SortableController from "./stimulus-components/sortable"
|
|
31
|
+
import SoundController from "./stimulus-components/sound"
|
|
32
|
+
import SpeechRecognitionController from "./stimulus-components/speech-recognition"
|
|
33
|
+
import TextareaAutogrowController from "./stimulus-components/textarea-autogrow"
|
|
34
|
+
import TimeagoController from "./stimulus-components/timeago"
|
|
35
|
+
|
|
36
|
+
const application = Application.start()
|
|
37
|
+
|
|
38
|
+
application.register("animated-number", AnimatedNumberController)
|
|
39
|
+
application.register("auto-submit", AutoSubmitController)
|
|
40
|
+
application.register("carousel", CarouselController)
|
|
41
|
+
application.register("character-counter", CharacterCounterController)
|
|
42
|
+
application.register("chartjs", ChartjsController)
|
|
43
|
+
application.register("checkbox-select-all", CheckboxSelectAllController)
|
|
44
|
+
application.register("clipboard", ClipboardController)
|
|
45
|
+
application.register("color-picker", ColorPickerController)
|
|
46
|
+
application.register("confirmation", ConfirmationController)
|
|
47
|
+
application.register("content-loader", ContentLoaderController)
|
|
48
|
+
application.register("dialog", DialogController)
|
|
49
|
+
application.register("dropdown", DropdownController)
|
|
50
|
+
application.register("glow", GlowController)
|
|
51
|
+
application.register("hotkey", HotkeyController)
|
|
52
|
+
application.register("lightbox", LightboxController)
|
|
53
|
+
application.register("notification", NotificationController)
|
|
54
|
+
application.register("password-visibility", PasswordVisibilityController)
|
|
55
|
+
application.register("places-autocomplete", PlacesAutocompleteController)
|
|
56
|
+
application.register("popover", PopoverController)
|
|
57
|
+
application.register("prefetch", PrefetchController)
|
|
58
|
+
application.register("rails-nested-form", RailsNestedFormController)
|
|
59
|
+
application.register("read-more", ReadMoreController)
|
|
60
|
+
application.register("remote-rails", RemoteRailsController)
|
|
61
|
+
application.register("reveal", RevealController)
|
|
62
|
+
application.register("scroll-progress", ScrollProgressController)
|
|
63
|
+
application.register("scroll-reveal", ScrollRevealController)
|
|
64
|
+
application.register("scroll-to", ScrollToController)
|
|
65
|
+
application.register("sortable", SortableController)
|
|
66
|
+
application.register("sound", SoundController)
|
|
67
|
+
application.register("speech-recognition", SpeechRecognitionController)
|
|
68
|
+
application.register("textarea-autogrow", TextareaAutogrowController)
|
|
69
|
+
application.register("timeago", TimeagoController)
|
|
70
|
+
|
|
71
|
+
export { application }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
function debounce(callback: Function, delay: number) {
|
|
4
|
+
let timeout: number
|
|
5
|
+
return (...args) => {
|
|
6
|
+
clearTimeout(timeout)
|
|
7
|
+
timeout = setTimeout(() => callback.apply(this, args), delay)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default class TextareaAutogrow extends Controller<HTMLInputElement> {
|
|
12
|
+
declare onResize: EventListenerOrEventListenerObject
|
|
13
|
+
declare resizeDebounceDelayValue: number
|
|
14
|
+
|
|
15
|
+
static values = {
|
|
16
|
+
resizeDebounceDelay: {
|
|
17
|
+
type: Number,
|
|
18
|
+
default: 100,
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
initialize(): void {
|
|
23
|
+
this.autogrow = this.autogrow.bind(this)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
connect(): void {
|
|
27
|
+
this.element.style.overflow = "hidden"
|
|
28
|
+
const delay = this.resizeDebounceDelayValue
|
|
29
|
+
|
|
30
|
+
this.onResize = delay > 0 ? debounce(this.autogrow, delay) : this.autogrow
|
|
31
|
+
|
|
32
|
+
this.autogrow()
|
|
33
|
+
this.element.addEventListener("input", this.autogrow)
|
|
34
|
+
window.addEventListener("resize", this.onResize)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
disconnect(): void {
|
|
38
|
+
window.removeEventListener("resize", this.onResize)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
autogrow(): void {
|
|
42
|
+
this.element.style.height = "auto"
|
|
43
|
+
this.element.style.height = `${this.element.scrollHeight}px`
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { formatDistanceToNow } from "date-fns"
|
|
3
|
+
|
|
4
|
+
export default class Timeago extends Controller<HTMLTimeElement> {
|
|
5
|
+
declare isValid: boolean
|
|
6
|
+
declare refreshTimer: number
|
|
7
|
+
declare hasRefreshIntervalValue: boolean
|
|
8
|
+
declare datetimeValue: string
|
|
9
|
+
declare addSuffixValue: boolean
|
|
10
|
+
declare includeSecondsValue: boolean
|
|
11
|
+
declare refreshIntervalValue: number
|
|
12
|
+
|
|
13
|
+
static values = {
|
|
14
|
+
datetime: String,
|
|
15
|
+
refreshInterval: Number,
|
|
16
|
+
includeSeconds: Boolean,
|
|
17
|
+
addSuffix: Boolean,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
initialize(): void {
|
|
21
|
+
this.isValid = true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
connect(): void {
|
|
25
|
+
this.load()
|
|
26
|
+
|
|
27
|
+
if (this.hasRefreshIntervalValue && this.isValid) {
|
|
28
|
+
this.startRefreshing()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
disconnect(): void {
|
|
33
|
+
this.stopRefreshing()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
load(): void {
|
|
37
|
+
const datetime = this.datetimeValue
|
|
38
|
+
const date = Date.parse(datetime)
|
|
39
|
+
const options = {
|
|
40
|
+
includeSeconds: this.includeSecondsValue,
|
|
41
|
+
addSuffix: this.addSuffixValue,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Number.isNaN(date)) {
|
|
45
|
+
this.isValid = false
|
|
46
|
+
console.error(
|
|
47
|
+
`[@stimulus-components/timeago] Value given in 'data-timeago-datetime' is not a valid date (${datetime}).`
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.element.dateTime = datetime
|
|
52
|
+
this.element.innerHTML = this.isValid ? formatDistanceToNow(date, options) : datetime
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
startRefreshing(): void {
|
|
56
|
+
this.refreshTimer = setInterval(() => {
|
|
57
|
+
this.load()
|
|
58
|
+
}, this.refreshIntervalValue)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
stopRefreshing(): void {
|
|
62
|
+
if (this.refreshTimer) {
|
|
63
|
+
clearInterval(this.refreshTimer)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'stimulus-components/version'
|
|
2
|
+
require 'stimulus-components/engine'
|
|
3
|
+
|
|
4
|
+
module StimulusComponents
|
|
5
|
+
COMPONENTS = %w[
|
|
6
|
+
animated-number
|
|
7
|
+
auto-submit
|
|
8
|
+
carousel
|
|
9
|
+
character-counter
|
|
10
|
+
chartjs
|
|
11
|
+
checkbox-select-all
|
|
12
|
+
clipboard
|
|
13
|
+
color-picker
|
|
14
|
+
confirmation
|
|
15
|
+
content-loader
|
|
16
|
+
dialog
|
|
17
|
+
dropdown
|
|
18
|
+
glow
|
|
19
|
+
hotkey
|
|
20
|
+
lightbox
|
|
21
|
+
notification
|
|
22
|
+
password-visibility
|
|
23
|
+
places-autocomplete
|
|
24
|
+
popover
|
|
25
|
+
prefetch
|
|
26
|
+
rails-nested-form
|
|
27
|
+
read-more
|
|
28
|
+
remote-rails
|
|
29
|
+
reveal-controller
|
|
30
|
+
scroll-progress
|
|
31
|
+
scroll-reveal
|
|
32
|
+
scroll-to
|
|
33
|
+
sortable
|
|
34
|
+
sound
|
|
35
|
+
speech-recognition
|
|
36
|
+
textarea-autogrow
|
|
37
|
+
timeago
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
def self.components
|
|
41
|
+
COMPONENTS
|
|
42
|
+
end
|
|
43
|
+
end
|