File tree Expand file tree Collapse file tree 10 files changed +306
-0
lines changed
Expand file tree Collapse file tree 10 files changed +306
-0
lines changed Original file line number Diff line number Diff line change @@ -10,6 +10,10 @@ calendar:
1010 js_packages :
1111 - " mustache"
1212
13+ carousel :
14+ js_packages :
15+ - " embla-carousel"
16+
1317chart :
1418 js_packages :
1519 - " chart.js"
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ module RubyUI
4+ class Carousel < Base
5+ def initialize ( orientation : :horizontal , options : { } , **user_attrs )
6+ @orientation = orientation
7+ @options = options
8+
9+ super ( **user_attrs )
10+ end
11+
12+ def view_template ( &)
13+ div ( **attrs , &)
14+ end
15+
16+ private
17+
18+ def default_attrs
19+ {
20+ class : [ "relative group" , orientation_classes ] ,
21+ role : "region" ,
22+ aria_roledescription : "carousel" ,
23+ data : {
24+ controller : "ruby-ui--carousel" ,
25+ ruby_ui__carousel_options_value : default_options . merge ( @options ) . to_json ,
26+ action : %w[
27+ keydown.right->ruby-ui--carousel#scrollNext:prevent
28+ keydown.left->ruby-ui--carousel#scrollPrev:prevent
29+ ]
30+ }
31+ }
32+ end
33+
34+ def default_options
35+ {
36+ axis : ( @orientation == :horizontal ) ? "x" : "y"
37+ }
38+ end
39+
40+ def orientation_classes
41+ ( @orientation == :horizontal ) ? "is-horizontal" : "is-vertical"
42+ end
43+ end
44+ end
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ module RubyUI
4+ class CarouselContent < Base
5+ def view_template ( &)
6+ div ( class : "overflow-hidden" , data : { ruby_ui__carousel_target : "viewport" } ) do
7+ div ( **attrs , &)
8+ end
9+ end
10+
11+ private
12+
13+ def default_attrs
14+ {
15+ class : [
16+ "flex" ,
17+ "group-[.is-horizontal]:-ml-4" ,
18+ "group-[.is-vertical]:-mt-4 group-[.is-vertical]:flex-col"
19+ ]
20+ }
21+ end
22+ end
23+ end
Original file line number Diff line number Diff line change 1+ import { Controller } from "@hotwired/stimulus" ;
2+ import EmblaCarousel from 'embla-carousel'
3+
4+ const DEFAULT_OPTIONS = {
5+ loop : true
6+ }
7+
8+ export default class extends Controller {
9+ static values = {
10+ options : {
11+ type : Object ,
12+ default : { } ,
13+ }
14+ }
15+ static targets = [ "viewport" , "nextButton" , "prevButton" ]
16+
17+ connect ( ) {
18+ this . initCarousel ( this . #mergedOptions)
19+ }
20+
21+ disconnect ( ) {
22+ this . destroyCarousel ( )
23+ }
24+
25+ initCarousel ( options , plugins = [ ] ) {
26+ this . carousel = EmblaCarousel ( this . viewportTarget , options , plugins )
27+
28+ this . carousel . on ( "init" , this . #updateControls. bind ( this ) )
29+ this . carousel . on ( "reInit" , this . #updateControls. bind ( this ) )
30+ this . carousel . on ( "select" , this . #updateControls. bind ( this ) )
31+ }
32+
33+ destroyCarousel ( ) {
34+ this . carousel . destroy ( )
35+ }
36+
37+ scrollNext ( ) {
38+ this . carousel . scrollNext ( )
39+ }
40+
41+ scrollPrev ( ) {
42+ this . carousel . scrollPrev ( )
43+ }
44+
45+ #updateControls( ) {
46+ this . #toggleButtonsDisabledState( this . nextButtonTargets , ! this . carousel . canScrollNext ( ) )
47+ this . #toggleButtonsDisabledState( this . prevButtonTargets , ! this . carousel . canScrollPrev ( ) )
48+ }
49+
50+ #toggleButtonsDisabledState( buttons , isDisabled ) {
51+ buttons . forEach ( ( button ) => button . disabled = isDisabled )
52+ }
53+
54+ get #mergedOptions( ) {
55+ return {
56+ ...DEFAULT_OPTIONS ,
57+ ...this . optionsValue
58+ }
59+ }
60+ }
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ module RubyUI
4+ class CarouselItem < Base
5+ def view_template ( &)
6+ div ( **attrs , &)
7+ end
8+
9+ private
10+
11+ def default_attrs
12+ {
13+ role : "group" ,
14+ aria_roledescription : "slide" ,
15+ class : [
16+ "min-w-0 shrink-0 grow-0 basis-full" ,
17+ "group-[.is-horizontal]:pl-4" ,
18+ "group-[.is-vertical]:pt-4"
19+ ]
20+ }
21+ end
22+ end
23+ end
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ module RubyUI
4+ class CarouselNext < Base
5+ def view_template ( &)
6+ Button ( **attrs ) do
7+ icon
8+ end
9+ end
10+
11+ private
12+
13+ def default_attrs
14+ {
15+ variant : :outline ,
16+ icon : true ,
17+ class : [
18+ "absolute h-8 w-8 rounded-full" ,
19+ "group-[.is-horizontal]:-right-12 group-[.is-horizontal]:top-1/2 group-[.is-horizontal]:-translate-y-1/2" ,
20+ "group-[.is-vertical]:-bottom-12 group-[.is-vertical]:left-1/2 group-[.is-vertical]:-translate-x-1/2 group-[.is-vertical]:rotate-90"
21+ ] ,
22+ disabled : true ,
23+ data : {
24+ action : "click->ruby-ui--carousel#scrollNext" ,
25+ ruby_ui__carousel_target : "nextButton"
26+ }
27+ }
28+ end
29+
30+ def icon
31+ svg (
32+ width : "24" ,
33+ height : "24" ,
34+ viewBox : "0 0 24 24" ,
35+ fill : "none" ,
36+ stroke : "currentColor" ,
37+ stroke_width : "2" ,
38+ stroke_linecap : "round" ,
39+ stroke_linejoin : "round" ,
40+ xmlns : "http://www.w3.org/2000/svg" ,
41+ class : "w-4 h-4"
42+ ) do |s |
43+ s . path ( d : "M5 12h14" )
44+ s . path ( d : "m12 5 7 7-7 7" )
45+ end
46+ end
47+ end
48+ end
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ module RubyUI
4+ class CarouselPrevious < Base
5+ def view_template ( &)
6+ Button ( **attrs ) do
7+ icon
8+ span ( class : "sr-only" ) { "Next slide" }
9+ end
10+ end
11+
12+ private
13+
14+ def default_attrs
15+ {
16+ variant : :outline ,
17+ icon : true ,
18+ class : [
19+ "absolute h-8 w-8 rounded-full" ,
20+ "group-[.is-horizontal]:-left-12 group-[.is-horizontal]:top-1/2 group-[.is-horizontal]:-translate-y-1/2" ,
21+ "group-[.is-vertical]:-top-12 group-[.is-vertical]:left-1/2 group-[.is-vertical]:-translate-x-1/2 group-[.is-vertical]:rotate-90"
22+ ] ,
23+ disabled : true ,
24+ data : {
25+ action : "click->ruby-ui--carousel#scrollPrev" ,
26+ ruby_ui__carousel_target : "prevButton"
27+ }
28+ }
29+ end
30+
31+ def icon
32+ svg (
33+ width : "24" ,
34+ height : "24" ,
35+ viewBox : "0 0 24 24" ,
36+ fill : "none" ,
37+ stroke : "currentColor" ,
38+ stroke_width : "2" ,
39+ stroke_linecap : "round" ,
40+ stroke_linejoin : "round" ,
41+ xmlns : "http://www.w3.org/2000/svg" ,
42+ class : "w-4 h-4"
43+ ) do |s |
44+ s . path ( d : "m12 19-7-7 7-7" )
45+ s . path ( d : "M19 12H5" )
46+ end
47+ end
48+ end
49+ end
Original file line number Diff line number Diff line change 2727 "@hotwired/stimulus" : " ^3.2.2" ,
2828 "chart.js" : " ^4.4.1" ,
2929 "date-fns" : " ^2.30.0" ,
30+ "embla-carousel" : " 8.5.2" ,
3031 "fuse.js" : " ^7.0.0" ,
3132 "maska" : " ^3.0.3" ,
3233 "motion" : " ^10.16.4" ,
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ require "test_helper"
4+
5+ class RubyUI ::CarouselTest < ComponentTest
6+ def test_render_with_all_items
7+ output = phlex do
8+ RubyUI . Carousel do
9+ RubyUI . CarouselContent do
10+ RubyUI . CarouselItem { "Item" }
11+ end
12+ RubyUI . CarouselPrevious ( )
13+ RubyUI . CarouselNext ( )
14+ end
15+ end
16+
17+ assert_match ( /Item/ , output )
18+ assert_match ( /button/ , output )
19+ assert_match ( / is-horizontal/ , output )
20+ end
21+
22+ def test_render_with_horizontal_orientation
23+ output = phlex do
24+ RubyUI . Carousel ( orientation : :horizontal ) do
25+ RubyUI . CarouselContent ( ) do
26+ RubyUI . CarouselItem ( ) { "Item" }
27+ end
28+ RubyUI . CarouselPrevious ( )
29+ RubyUI . CarouselNext ( )
30+ end
31+ end
32+
33+ assert_match ( / is-horizontal/ , output )
34+ end
35+
36+ def test_render_with_vertical_orientation
37+ output = phlex do
38+ RubyUI . Carousel ( orientation : :vertical ) do
39+ RubyUI . CarouselContent ( ) do
40+ RubyUI . CarouselItem ( ) { "Item" }
41+ end
42+ RubyUI . CarouselPrevious ( )
43+ RubyUI . CarouselNext ( )
44+ end
45+ end
46+
47+ assert_match ( / is-vertical/ , output )
48+ end
49+ end
Original file line number Diff line number Diff line change @@ -111,6 +111,11 @@ date-fns@^2.30.0:
111111 dependencies :
112112 " @babel/runtime" " ^7.21.0"
113113
114+ 115+ version "8.5.2"
116+ resolved "https://registry.yarnpkg.com/embla-carousel/-/embla-carousel-8.5.2.tgz#95eb936d14a1b9a67b9207a0fde1f25259a5d692"
117+ integrity sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==
118+
114119fuse.js@^7.0.0 :
115120 version "7.0.0"
116121 resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
You can’t perform that action at this time.
0 commit comments