katalyst-kpop 2.0.9 → 3.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -43
- data/app/assets/builds/katalyst/kpop.esm.js +599 -0
- data/app/assets/builds/katalyst/kpop.js +479 -519
- data/app/assets/builds/katalyst/kpop.min.js +2 -1
- data/app/assets/builds/katalyst/kpop.min.js.map +1 -0
- data/app/assets/builds/katalyst/kpop.umd.js +5890 -0
- data/app/assets/config/kpop.js +1 -1
- data/app/assets/stylesheets/katalyst/kpop/_frame.scss +104 -0
- data/app/assets/stylesheets/katalyst/kpop/_modal.scss +95 -0
- data/app/assets/stylesheets/katalyst/kpop/_scrim.scss +33 -3
- data/app/assets/stylesheets/katalyst/kpop/_side_panel.scss +64 -0
- data/app/assets/stylesheets/katalyst/kpop/_variables.scss +25 -0
- data/app/assets/stylesheets/katalyst/kpop.scss +6 -1
- data/app/components/concerns/kpop/has_html_attributes.rb +78 -0
- data/app/components/kpop/frame_component.html.erb +14 -0
- data/app/components/kpop/frame_component.rb +45 -0
- data/app/components/kpop/modal/title_component.html.erb +6 -0
- data/app/components/kpop/modal/title_component.rb +28 -0
- data/app/components/kpop/modal_component.html.erb +8 -0
- data/app/components/kpop/modal_component.rb +39 -0
- data/app/components/scrim_component.rb +32 -0
- data/app/helpers/kpop_helper.rb +12 -35
- data/app/javascript/kpop/application.js +15 -0
- data/app/javascript/kpop/controllers/close_controller.js +9 -0
- data/app/javascript/kpop/controllers/frame_controller.js +189 -0
- data/app/javascript/kpop/controllers/modal_controller.js +30 -0
- data/app/javascript/kpop/controllers/redirect_controller.js +22 -0
- data/app/{assets/javascripts → javascript/kpop}/controllers/scrim_controller.js +76 -72
- data/app/javascript/kpop/debug.js +3 -0
- data/app/javascript/kpop/modals/content_modal.js +46 -0
- data/app/javascript/kpop/modals/frame_modal.js +41 -0
- data/app/javascript/kpop/modals/modal.js +69 -0
- data/app/javascript/kpop/modals/stream_modal.js +49 -0
- data/app/javascript/kpop/modals/stream_renderer.js +15 -0
- data/app/views/layouts/kpop.html.erb +1 -1
- data/config/importmap.rb +1 -4
- data/lib/katalyst/kpop/engine.rb +13 -12
- data/lib/katalyst/kpop/matchers/base.rb +18 -0
- data/lib/katalyst/kpop/matchers/capybara_matcher.rb +46 -0
- data/lib/katalyst/kpop/matchers/capybara_parser.rb +17 -0
- data/lib/katalyst/kpop/matchers/chained_matcher.rb +40 -0
- data/lib/katalyst/kpop/matchers/frame_matcher.rb +16 -0
- data/lib/katalyst/kpop/matchers/modal_matcher.rb +20 -0
- data/lib/katalyst/kpop/matchers/redirect_finder.rb +16 -0
- data/lib/katalyst/kpop/matchers/redirect_matcher.rb +28 -0
- data/lib/katalyst/kpop/matchers/response_matcher.rb +33 -0
- data/lib/katalyst/kpop/matchers/stream_matcher.rb +16 -0
- data/lib/katalyst/kpop/matchers/title_finder.rb +16 -0
- data/lib/katalyst/kpop/matchers/title_matcher.rb +28 -0
- data/lib/katalyst/kpop/matchers.rb +79 -0
- data/lib/katalyst/kpop/turbo.rb +56 -0
- data/lib/katalyst/kpop/version.rb +1 -1
- data/lib/katalyst/kpop.rb +4 -0
- metadata +90 -15
- data/app/assets/builds/katalyst/kpop.css +0 -117
- data/app/assets/javascripts/controllers/kpop_controller.js +0 -72
- data/app/assets/javascripts/katalyst/kpop.js +0 -9
- data/app/assets/stylesheets/katalyst/kpop/_index.scss +0 -2
- data/app/assets/stylesheets/katalyst/kpop/_kpop.scss +0 -133
- data/app/helpers/kpop/modal.rb +0 -98
- data/app/helpers/scrim_helper.rb +0 -13
data/app/assets/config/kpop.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
//= link_tree ../
|
1
|
+
//= link_tree ../builds
|
@@ -0,0 +1,104 @@
|
|
1
|
+
@use "variables" as *;
|
2
|
+
|
3
|
+
.kpop--container {
|
4
|
+
display: none;
|
5
|
+
|
6
|
+
position: fixed;
|
7
|
+
left: 0;
|
8
|
+
top: 0;
|
9
|
+
right: 0;
|
10
|
+
bottom: 0;
|
11
|
+
|
12
|
+
align-items: center;
|
13
|
+
z-index: 100;
|
14
|
+
pointer-events: none;
|
15
|
+
|
16
|
+
> * {
|
17
|
+
pointer-events: auto;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
.kpop--frame {
|
22
|
+
--opening-animation: slide-in-up;
|
23
|
+
--closing-animation: slide-out-down;
|
24
|
+
|
25
|
+
position: relative;
|
26
|
+
display: grid;
|
27
|
+
overflow: hidden;
|
28
|
+
margin: 0 auto;
|
29
|
+
|
30
|
+
--min-width: #{$min-width};
|
31
|
+
--max-width: #{$max-width};
|
32
|
+
--min-height: #{$min-height};
|
33
|
+
--max-height: #{$max-height};
|
34
|
+
|
35
|
+
min-width: var(--min-width);
|
36
|
+
max-width: var(--max-width);
|
37
|
+
min-height: var(--min-height);
|
38
|
+
max-height: var(--max-height);
|
39
|
+
|
40
|
+
grid-template-columns: min(var(--max-width), max(var(--min-width), 100%));
|
41
|
+
grid-template-rows: min(var(--max-height), max(var(--min-height), 100%));
|
42
|
+
}
|
43
|
+
|
44
|
+
@media (max-width: $mobile-width), (max-height: $mobile-height) {
|
45
|
+
.kpop--frame {
|
46
|
+
--min-width: 100vw;
|
47
|
+
--max-width: 100vw;
|
48
|
+
--min-height: 50vh;
|
49
|
+
--max-height: calc(100vh - 1.5rem);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
.scrim[data-scrim-open-value="false"] + .kpop--container .kpop--frame {
|
54
|
+
display: none;
|
55
|
+
}
|
56
|
+
|
57
|
+
.scrim[data-hide-animating]
|
58
|
+
+ .kpop--container
|
59
|
+
.kpop--frame[data-kpop--frame-open-value="true"] {
|
60
|
+
animation: var(--closing-animation);
|
61
|
+
animation-duration: $duration;
|
62
|
+
animation-fill-mode: forwards;
|
63
|
+
}
|
64
|
+
|
65
|
+
.scrim[data-show-animating] + .kpop--container .kpop--frame {
|
66
|
+
animation: var(--opening-animation);
|
67
|
+
animation-duration: $duration;
|
68
|
+
animation-fill-mode: forwards;
|
69
|
+
}
|
70
|
+
|
71
|
+
.kpop-modal.iframe {
|
72
|
+
.kpop-content {
|
73
|
+
overflow: unset;
|
74
|
+
}
|
75
|
+
|
76
|
+
iframe {
|
77
|
+
height: $max-height;
|
78
|
+
width: $max-width;
|
79
|
+
flex-grow: 1;
|
80
|
+
overflow: scroll;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
@keyframes slide-in-up {
|
85
|
+
0% {
|
86
|
+
transform: translateY(10%);
|
87
|
+
opacity: 0;
|
88
|
+
}
|
89
|
+
100% {
|
90
|
+
transform: translateY(0%);
|
91
|
+
opacity: 1;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
@keyframes slide-out-down {
|
96
|
+
0% {
|
97
|
+
transform: translateY(0%);
|
98
|
+
opacity: 1;
|
99
|
+
}
|
100
|
+
100% {
|
101
|
+
transform: translateY(10%);
|
102
|
+
opacity: 0;
|
103
|
+
}
|
104
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
@use "variables" as *;
|
2
|
+
|
3
|
+
.kpop-modal {
|
4
|
+
display: grid;
|
5
|
+
grid-template-areas:
|
6
|
+
"title-bar"
|
7
|
+
"header"
|
8
|
+
"content"
|
9
|
+
"footer";
|
10
|
+
grid-template-rows: auto auto 1fr auto;
|
11
|
+
|
12
|
+
background-color: white;
|
13
|
+
border: 1px solid black;
|
14
|
+
border-radius: $border-radius;
|
15
|
+
}
|
16
|
+
|
17
|
+
.kpop-title-bar {
|
18
|
+
grid-area: title-bar;
|
19
|
+
display: flex;
|
20
|
+
background: $title-background-color;
|
21
|
+
color: $title-text-color;
|
22
|
+
|
23
|
+
.kpop-title {
|
24
|
+
padding: $default-padding;
|
25
|
+
flex-grow: 1;
|
26
|
+
}
|
27
|
+
|
28
|
+
.kpop-close {
|
29
|
+
background: none;
|
30
|
+
border: none;
|
31
|
+
color: $title-text-color;
|
32
|
+
cursor: pointer;
|
33
|
+
display: block;
|
34
|
+
font-size: 2rem;
|
35
|
+
font-weight: bold;
|
36
|
+
padding: 0 0.75rem;
|
37
|
+
text-decoration: none;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
.kpop-header {
|
42
|
+
grid-area: header;
|
43
|
+
}
|
44
|
+
|
45
|
+
.kpop-content {
|
46
|
+
grid-area: content;
|
47
|
+
display: flex;
|
48
|
+
flex-direction: column;
|
49
|
+
overflow: auto;
|
50
|
+
}
|
51
|
+
|
52
|
+
.kpop-footer {
|
53
|
+
grid-area: footer;
|
54
|
+
background: white;
|
55
|
+
border-top: 1px solid black;
|
56
|
+
padding: $default-padding;
|
57
|
+
}
|
58
|
+
|
59
|
+
.kpop-buttons {
|
60
|
+
display: flex;
|
61
|
+
gap: 0.5rem;
|
62
|
+
justify-content: space-between;
|
63
|
+
}
|
64
|
+
|
65
|
+
.kpop-modal.iframe {
|
66
|
+
.kpop-content {
|
67
|
+
overflow: unset;
|
68
|
+
}
|
69
|
+
|
70
|
+
iframe {
|
71
|
+
height: $max-height;
|
72
|
+
width: $max-width;
|
73
|
+
flex-grow: 1;
|
74
|
+
overflow: scroll;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
@include mobile {
|
79
|
+
.kpop-modal {
|
80
|
+
border-radius: 0;
|
81
|
+
border: none;
|
82
|
+
}
|
83
|
+
|
84
|
+
.kpop-modal.iframe {
|
85
|
+
iframe {
|
86
|
+
width: 100%;
|
87
|
+
height: 100%;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
.kpop-buttons {
|
92
|
+
flex-direction: column-reverse;
|
93
|
+
text-align: center;
|
94
|
+
}
|
95
|
+
}
|
@@ -1,16 +1,46 @@
|
|
1
|
+
@use "variables" as *;
|
2
|
+
|
1
3
|
.scrim {
|
2
4
|
position: fixed;
|
3
5
|
top: 0;
|
4
6
|
bottom: 0;
|
5
7
|
left: 0;
|
6
8
|
right: 0;
|
7
|
-
background:
|
9
|
+
background: $scrim-background;
|
8
10
|
z-index: -1;
|
9
|
-
transition: opacity 0.2s ease-in-out, z-index 0.2s step-end;
|
10
11
|
opacity: 0;
|
12
|
+
|
13
|
+
&[data-hide-animating] {
|
14
|
+
animation: fade-out;
|
15
|
+
animation-duration: $duration;
|
16
|
+
animation-fill-mode: forwards;
|
17
|
+
}
|
18
|
+
|
19
|
+
&[data-show-animating] {
|
20
|
+
animation: fade-in;
|
21
|
+
animation-duration: $duration;
|
22
|
+
animation-fill-mode: forwards;
|
23
|
+
}
|
11
24
|
}
|
12
25
|
|
13
26
|
.scrim[data-scrim-open-value="true"] {
|
14
27
|
opacity: 1;
|
15
|
-
|
28
|
+
}
|
29
|
+
|
30
|
+
@keyframes fade-in {
|
31
|
+
0% {
|
32
|
+
opacity: 0;
|
33
|
+
}
|
34
|
+
100% {
|
35
|
+
opacity: 1;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
@keyframes fade-out {
|
40
|
+
0% {
|
41
|
+
opacity: 1;
|
42
|
+
}
|
43
|
+
100% {
|
44
|
+
opacity: 0;
|
45
|
+
}
|
16
46
|
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
@use "variables" as *;
|
2
|
+
|
3
|
+
.kpop--frame.side-panel {
|
4
|
+
--opening-animation: slide-in-right;
|
5
|
+
--closing-animation: slide-out-right;
|
6
|
+
--min-width: 30vw;
|
7
|
+
--max-width: 50vw;
|
8
|
+
--min-height: 100vh;
|
9
|
+
--max-height: 100vh;
|
10
|
+
|
11
|
+
margin-inline: auto 0;
|
12
|
+
align-self: flex-end;
|
13
|
+
|
14
|
+
@include mobile {
|
15
|
+
& {
|
16
|
+
--opening-animation: slide-in-bottom;
|
17
|
+
--closing-animation: slide-out-bottom;
|
18
|
+
--min-width: 100vw;
|
19
|
+
--max-width: 100vw;
|
20
|
+
--min-height: 50vh;
|
21
|
+
--max-height: calc(100vh - 1.5rem);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
.kpop-modal {
|
26
|
+
border-radius: 0;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
@keyframes slide-in-right {
|
31
|
+
0% {
|
32
|
+
transform: translateX(100%);
|
33
|
+
}
|
34
|
+
100% {
|
35
|
+
transform: translateX(0%);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
@keyframes slide-out-right {
|
40
|
+
0% {
|
41
|
+
transform: translateX(0%);
|
42
|
+
}
|
43
|
+
100% {
|
44
|
+
transform: translateX(100%);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
@keyframes slide-in-bottom {
|
49
|
+
0% {
|
50
|
+
transform: translateY(100%);
|
51
|
+
}
|
52
|
+
100% {
|
53
|
+
transform: translateY(0%);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
@keyframes slide-out-bottom {
|
58
|
+
0% {
|
59
|
+
transform: translateY(0%);
|
60
|
+
}
|
61
|
+
100% {
|
62
|
+
transform: translateY(100%);
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
// frame variables
|
2
|
+
$min-width: 35rem !default;
|
3
|
+
$max-width: 52rem !default;
|
4
|
+
$min-height: 30vh !default;
|
5
|
+
$max-height: 80vh !default;
|
6
|
+
$duration: 0.2s !default;
|
7
|
+
|
8
|
+
// breakpoints
|
9
|
+
$mobile-width: 600px !default;
|
10
|
+
$mobile-height: 400px !default;
|
11
|
+
|
12
|
+
@mixin mobile {
|
13
|
+
@media (max-width: $mobile-width), (max-height: $mobile-height) {
|
14
|
+
@content;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
// modal variables
|
19
|
+
$border-radius: 0.5rem !default;
|
20
|
+
$title-background-color: #344055 !default;
|
21
|
+
$title-text-color: white !default;
|
22
|
+
$default-padding: 1rem 1.5rem !default;
|
23
|
+
|
24
|
+
// scrim variables
|
25
|
+
$scrim-background: rgba(0, 0, 0, 0.6) !default;
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "html_attributes_utils"
|
4
|
+
|
5
|
+
module Kpop
|
6
|
+
# Adds HTML attributes to a component.
|
7
|
+
# Accepts HTML attributes from the constructor or via `html_attributes=`.
|
8
|
+
# These are merged with the default attributes defined in the component.
|
9
|
+
# Adds support for custom html attributes for other tags, e.g.:
|
10
|
+
# define_html_attribute_methods :table_attributes, default: {}
|
11
|
+
# tag.table(**table_attributes)
|
12
|
+
module HasHtmlAttributes
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
using HTMLAttributesUtils
|
16
|
+
|
17
|
+
MERGEABLE_ATTRIBUTES = [
|
18
|
+
*HTMLAttributesUtils::DEFAULT_MERGEABLE_ATTRIBUTES,
|
19
|
+
%i[data controller],
|
20
|
+
%i[data action],
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
FLATTENABLE_ATTRIBUTES = [
|
24
|
+
%i[data controller],
|
25
|
+
%i[data action],
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
refine NilClass do
|
29
|
+
def flatten_html(*)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
refine Hash do
|
35
|
+
def merge_html(attributes)
|
36
|
+
result = deep_merge_html_attributes(attributes, mergeable_attributes: MERGEABLE_ATTRIBUTES)
|
37
|
+
FLATTENABLE_ATTRIBUTES.each_with_object(result) do |path, flattened|
|
38
|
+
flattened.flatten_html(*path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def flatten_html(key, *path)
|
43
|
+
if path.empty?
|
44
|
+
self[key] = self[key].join(" ") if self[key].is_a?(Array)
|
45
|
+
else
|
46
|
+
self[key].flatten_html(*path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class_methods do
|
52
|
+
using HasHtmlAttributes
|
53
|
+
|
54
|
+
def define_html_attribute_methods(name, default: {})
|
55
|
+
define_method("default_#{name}") { default }
|
56
|
+
private("default_#{name}")
|
57
|
+
|
58
|
+
define_method(name) do
|
59
|
+
send("default_#{name}").merge_html(instance_variable_get("@#{name}") || {})
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method("#{name}=") do |options|
|
63
|
+
instance_variable_set("@#{name}", options.slice(:id, :aria, :class, :data).merge(options.fetch(:html, {})))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
included do
|
69
|
+
define_html_attribute_methods :html_attributes, default: {}
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(**options)
|
73
|
+
super(**options.except(:id, :aria, :class, :data, :html))
|
74
|
+
|
75
|
+
self.html_attributes = options
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="kpop--container">
|
2
|
+
<%= turbo_frame_tag(id, **html_attributes) do %>
|
3
|
+
<%= content %>
|
4
|
+
<% if flash[:kpop] %>
|
5
|
+
<%= tag.div("", data: {
|
6
|
+
controller: "kpop--redirect",
|
7
|
+
kpop__redirect_kpop__frame_outlet: "##{id}",
|
8
|
+
kpop__redirect_path_value: flash[:kpop],
|
9
|
+
kpop__redirect_target_value: id,
|
10
|
+
turbo_temporary: "",
|
11
|
+
}) %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kpop
|
4
|
+
class FrameComponent < ViewComponent::Base
|
5
|
+
include HasHtmlAttributes
|
6
|
+
include Turbo::FramesHelper
|
7
|
+
|
8
|
+
attr_reader :id
|
9
|
+
|
10
|
+
ACTIONS = %w[
|
11
|
+
popstate@window->kpop--frame#popstate
|
12
|
+
scrim:dismiss@window->kpop--frame#dismiss
|
13
|
+
scrim:hide@window->kpop--frame#dismiss
|
14
|
+
turbo:before-frame-render->kpop--frame#beforeFrameRender
|
15
|
+
turbo:before-visit@window->kpop--frame#beforeVisit
|
16
|
+
turbo:frame-load->kpop--frame#frameLoad
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
def initialize(id: "kpop", scrim: "#scrim", **)
|
20
|
+
super
|
21
|
+
|
22
|
+
@id = id
|
23
|
+
@scrim = scrim
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"#<#{self.class.name} id: #{id.inspect}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def default_html_attributes
|
33
|
+
{
|
34
|
+
class: "kpop--frame",
|
35
|
+
data: {
|
36
|
+
controller: "kpop--frame",
|
37
|
+
action: ACTIONS.join(" "),
|
38
|
+
"kpop--frame-scrim-outlet": @scrim,
|
39
|
+
turbo_action: "advance",
|
40
|
+
},
|
41
|
+
target: "_top",
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kpop
|
4
|
+
module Modal
|
5
|
+
class TitleComponent < ViewComponent::Base
|
6
|
+
include HasHtmlAttributes
|
7
|
+
|
8
|
+
def initialize(title: nil, captive: false, **)
|
9
|
+
super
|
10
|
+
|
11
|
+
@title = title
|
12
|
+
@captive = captive
|
13
|
+
end
|
14
|
+
|
15
|
+
def title
|
16
|
+
content? ? content : @title
|
17
|
+
end
|
18
|
+
|
19
|
+
def captive?
|
20
|
+
@captive
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class.name} title: #{title.inspect}>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kpop
|
4
|
+
class ModalComponent < ViewComponent::Base
|
5
|
+
include HasHtmlAttributes
|
6
|
+
|
7
|
+
renders_one :title, "Kpop::Modal::TitleComponent"
|
8
|
+
renders_one :header
|
9
|
+
renders_one :footer
|
10
|
+
|
11
|
+
def initialize(title:, captive: false, fallback_location: nil, layout: nil, **)
|
12
|
+
super
|
13
|
+
|
14
|
+
@fallback_location = fallback_location
|
15
|
+
@layout = layout
|
16
|
+
|
17
|
+
# Generate a title bar. This can be overridden by calling title_bar again.
|
18
|
+
with_title(title:, captive:) if title.present?
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<#{self.class.name} title: #{title.inspect}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_html_attributes
|
28
|
+
{
|
29
|
+
class: "kpop-modal",
|
30
|
+
data: {
|
31
|
+
controller: "kpop--modal",
|
32
|
+
"kpop--modal-current-location-value": request.path,
|
33
|
+
"kpop--modal-fallback-location-value": @fallback_location,
|
34
|
+
"kpop--modal-layout-value": @layout&.to_s&.dasherize,
|
35
|
+
},
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ScrimComponent < ViewComponent::Base
|
4
|
+
attr_reader :id, :z_index
|
5
|
+
|
6
|
+
ACTIONS = %w[
|
7
|
+
click->scrim#dismiss
|
8
|
+
keyup@window->scrim#escape
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
def initialize(id: "scrim", z_index: 40)
|
12
|
+
super
|
13
|
+
|
14
|
+
@id = id
|
15
|
+
@z_index = z_index
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
tag.div(id:,
|
20
|
+
class: "scrim",
|
21
|
+
data: {
|
22
|
+
controller: "scrim",
|
23
|
+
scrim_z_index_value: z_index,
|
24
|
+
turbo_permanent: "",
|
25
|
+
action: ACTIONS.join(" "),
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#<#{self.class.name} id: #{id.inspect}>"
|
31
|
+
end
|
32
|
+
end
|
data/app/helpers/kpop_helper.rb
CHANGED
@@ -1,55 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module KpopHelper
|
4
|
-
|
5
|
-
# See builder for options.
|
6
|
-
def render_kpop(options = {}, &block)
|
7
|
-
Kpop::Modal.new(self).render(options, &block)
|
8
|
-
end
|
9
|
-
|
10
|
-
# Render a turbo stream action that will dismiss any open kpop modals.
|
11
|
-
def dismiss_kpop
|
12
|
-
turbo_stream.update("kpop", "")
|
13
|
-
end
|
14
|
-
|
15
|
-
# Render a turbo frame tag that can be targeted for rendering kpop modals.
|
16
|
-
def kpop_frame_tag(**html_attributes, &block)
|
17
|
-
html_attributes[:class] ||= "kpop-container"
|
18
|
-
html_attributes[:data] ||= {}
|
19
|
-
html_attributes[:data][:controller] = "kpop"
|
20
|
-
html_attributes[:data][:action] = "scrim:hide@window->kpop#dismiss"
|
21
|
-
|
22
|
-
turbo_frame_tag("kpop", **html_attributes) do
|
23
|
-
capture(&block) if block
|
24
|
-
end
|
25
|
-
end
|
4
|
+
using HTMLAttributesUtils
|
26
5
|
|
27
6
|
# Renders a link that will navigate the kpop turbo frame to the given URL.
|
28
7
|
# The URL should render a modal response inside a kpop frame tag.
|
29
|
-
def kpop_link_to(name = nil, options = nil,
|
30
|
-
|
31
|
-
data: { turbo: true, turbo_frame: "kpop" },
|
32
|
-
}
|
8
|
+
def kpop_link_to(name = nil, options = nil, html_attributes = nil, &block)
|
9
|
+
default_html_attributes = { data: { turbo_frame: "kpop" } }
|
33
10
|
if block
|
34
11
|
# Param[name] is the path for the link
|
35
|
-
link_to(name,
|
12
|
+
link_to(name, default_html_attributes.deep_merge_html_attributes(options || {}), &block)
|
36
13
|
else
|
37
|
-
link_to(name, options,
|
14
|
+
link_to(name, options, default_html_attributes.deep_merge_html_attributes(html_attributes || {}))
|
38
15
|
end
|
39
16
|
end
|
40
17
|
|
41
18
|
# Renders a button that will navigate the kpop turbo frame to the given URL.
|
42
19
|
# The URL should render a modal response inside a kpop frame tag.
|
43
|
-
def kpop_button_to(name = nil, options = nil,
|
44
|
-
|
45
|
-
form: { data: {
|
20
|
+
def kpop_button_to(name = nil, options = nil, html_attributes = nil, &)
|
21
|
+
default_html_attributes = {
|
22
|
+
form: { data: { turbo_frame: "kpop" } },
|
46
23
|
}
|
47
|
-
button_to(name, options,
|
24
|
+
button_to(name, options, default_html_attributes.deep_merge_html_attributes(html_attributes || {}), &)
|
48
25
|
end
|
49
26
|
|
50
27
|
# Renders a button that will close the current kpop modal, if any.
|
51
|
-
def kpop_button_close(content = nil,
|
52
|
-
content =
|
53
|
-
tag.button
|
28
|
+
def kpop_button_close(content = nil, **, &block)
|
29
|
+
content = capture(yield) if block
|
30
|
+
tag.button(content, data: { action: "click->kpop--frame#dismiss:prevent" }, **)
|
54
31
|
end
|
55
32
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import CloseController from "../kpop/controllers/close_controller";
|
2
|
+
import FrameController from "../kpop/controllers/frame_controller";
|
3
|
+
import ModalController from "../kpop/controllers/modal_controller";
|
4
|
+
import RedirectController from "../kpop/controllers/redirect_controller";
|
5
|
+
import ScrimController from "../kpop/controllers/scrim_controller";
|
6
|
+
|
7
|
+
const Definitions = [
|
8
|
+
{ identifier: "kpop--close", controllerConstructor: CloseController },
|
9
|
+
{ identifier: "kpop--frame", controllerConstructor: FrameController },
|
10
|
+
{ identifier: "kpop--modal", controllerConstructor: ModalController },
|
11
|
+
{ identifier: "kpop--redirect", controllerConstructor: RedirectController },
|
12
|
+
{ identifier: "scrim", controllerConstructor: ScrimController },
|
13
|
+
];
|
14
|
+
|
15
|
+
export { Definitions as default };
|