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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -43
  3. data/app/assets/builds/katalyst/kpop.esm.js +599 -0
  4. data/app/assets/builds/katalyst/kpop.js +479 -519
  5. data/app/assets/builds/katalyst/kpop.min.js +2 -1
  6. data/app/assets/builds/katalyst/kpop.min.js.map +1 -0
  7. data/app/assets/builds/katalyst/kpop.umd.js +5890 -0
  8. data/app/assets/config/kpop.js +1 -1
  9. data/app/assets/stylesheets/katalyst/kpop/_frame.scss +104 -0
  10. data/app/assets/stylesheets/katalyst/kpop/_modal.scss +95 -0
  11. data/app/assets/stylesheets/katalyst/kpop/_scrim.scss +33 -3
  12. data/app/assets/stylesheets/katalyst/kpop/_side_panel.scss +64 -0
  13. data/app/assets/stylesheets/katalyst/kpop/_variables.scss +25 -0
  14. data/app/assets/stylesheets/katalyst/kpop.scss +6 -1
  15. data/app/components/concerns/kpop/has_html_attributes.rb +78 -0
  16. data/app/components/kpop/frame_component.html.erb +14 -0
  17. data/app/components/kpop/frame_component.rb +45 -0
  18. data/app/components/kpop/modal/title_component.html.erb +6 -0
  19. data/app/components/kpop/modal/title_component.rb +28 -0
  20. data/app/components/kpop/modal_component.html.erb +8 -0
  21. data/app/components/kpop/modal_component.rb +39 -0
  22. data/app/components/scrim_component.rb +32 -0
  23. data/app/helpers/kpop_helper.rb +12 -35
  24. data/app/javascript/kpop/application.js +15 -0
  25. data/app/javascript/kpop/controllers/close_controller.js +9 -0
  26. data/app/javascript/kpop/controllers/frame_controller.js +189 -0
  27. data/app/javascript/kpop/controllers/modal_controller.js +30 -0
  28. data/app/javascript/kpop/controllers/redirect_controller.js +22 -0
  29. data/app/{assets/javascripts → javascript/kpop}/controllers/scrim_controller.js +76 -72
  30. data/app/javascript/kpop/debug.js +3 -0
  31. data/app/javascript/kpop/modals/content_modal.js +46 -0
  32. data/app/javascript/kpop/modals/frame_modal.js +41 -0
  33. data/app/javascript/kpop/modals/modal.js +69 -0
  34. data/app/javascript/kpop/modals/stream_modal.js +49 -0
  35. data/app/javascript/kpop/modals/stream_renderer.js +15 -0
  36. data/app/views/layouts/kpop.html.erb +1 -1
  37. data/config/importmap.rb +1 -4
  38. data/lib/katalyst/kpop/engine.rb +13 -12
  39. data/lib/katalyst/kpop/matchers/base.rb +18 -0
  40. data/lib/katalyst/kpop/matchers/capybara_matcher.rb +46 -0
  41. data/lib/katalyst/kpop/matchers/capybara_parser.rb +17 -0
  42. data/lib/katalyst/kpop/matchers/chained_matcher.rb +40 -0
  43. data/lib/katalyst/kpop/matchers/frame_matcher.rb +16 -0
  44. data/lib/katalyst/kpop/matchers/modal_matcher.rb +20 -0
  45. data/lib/katalyst/kpop/matchers/redirect_finder.rb +16 -0
  46. data/lib/katalyst/kpop/matchers/redirect_matcher.rb +28 -0
  47. data/lib/katalyst/kpop/matchers/response_matcher.rb +33 -0
  48. data/lib/katalyst/kpop/matchers/stream_matcher.rb +16 -0
  49. data/lib/katalyst/kpop/matchers/title_finder.rb +16 -0
  50. data/lib/katalyst/kpop/matchers/title_matcher.rb +28 -0
  51. data/lib/katalyst/kpop/matchers.rb +79 -0
  52. data/lib/katalyst/kpop/turbo.rb +56 -0
  53. data/lib/katalyst/kpop/version.rb +1 -1
  54. data/lib/katalyst/kpop.rb +4 -0
  55. metadata +90 -15
  56. data/app/assets/builds/katalyst/kpop.css +0 -117
  57. data/app/assets/javascripts/controllers/kpop_controller.js +0 -72
  58. data/app/assets/javascripts/katalyst/kpop.js +0 -9
  59. data/app/assets/stylesheets/katalyst/kpop/_index.scss +0 -2
  60. data/app/assets/stylesheets/katalyst/kpop/_kpop.scss +0 -133
  61. data/app/helpers/kpop/modal.rb +0 -98
  62. data/app/helpers/scrim_helper.rb +0 -13
@@ -1,117 +0,0 @@
1
- .kpop-container {
2
- display: none;
3
- position: fixed;
4
- left: 0;
5
- top: 0;
6
- right: 0;
7
- bottom: 0;
8
- justify-content: center;
9
- align-items: center;
10
- z-index: 1000;
11
- pointer-events: none;
12
- }
13
- .kpop-container > * {
14
- pointer-events: auto;
15
- }
16
-
17
- .kpop-modal {
18
- position: relative;
19
- overflow: hidden;
20
- display: grid;
21
- grid-template-areas: "title-bar" "header" "content" "footer";
22
- grid-template-rows: auto auto 1fr auto;
23
- border: 1px solid black;
24
- border-radius: 0.5rem;
25
- background-color: white;
26
- min-width: 35rem;
27
- max-width: 52rem;
28
- min-height: 0;
29
- max-height: 80vh;
30
- }
31
- .kpop-modal .kpop-title-bar {
32
- grid-area: title-bar;
33
- display: flex;
34
- background: #344055;
35
- color: white;
36
- }
37
- .kpop-modal .kpop-title-bar .kpop-title {
38
- padding: 1rem 1.5rem;
39
- flex-grow: 1;
40
- }
41
- .kpop-modal .kpop-title-bar .kpop-close {
42
- background: none;
43
- border: none;
44
- color: white;
45
- cursor: pointer;
46
- display: block;
47
- font-size: 2rem;
48
- font-weight: bold;
49
- padding: 0 0.75rem;
50
- text-decoration: none;
51
- }
52
- .kpop-modal .kpop-header {
53
- grid-area: header;
54
- }
55
- .kpop-modal .kpop-content {
56
- grid-area: content;
57
- display: flex;
58
- flex-direction: column;
59
- overflow: auto;
60
- }
61
- .kpop-modal .kpop-footer {
62
- grid-area: footer;
63
- background: white;
64
- border-top: 1px solid black;
65
- padding: 1rem 1.5rem;
66
- }
67
- .kpop-modal .kpop-buttons {
68
- display: flex;
69
- gap: 0.5rem;
70
- justify-content: space-between;
71
- }
72
-
73
- .kpop-modal.iframe .kpop-content {
74
- overflow: unset;
75
- }
76
- .kpop-modal.iframe iframe {
77
- height: 80vh;
78
- width: 52rem;
79
- flex-grow: 1;
80
- overflow: scroll;
81
- }
82
-
83
- @media (max-width: 600px), (max-height: 600px) {
84
- .kpop-modal {
85
- max-width: unset;
86
- min-width: unset;
87
- width: 100%;
88
- height: 100%;
89
- max-height: 100vh;
90
- border-radius: 0;
91
- border: none;
92
- }
93
- .kpop-modal.iframe iframe {
94
- width: 100%;
95
- height: 100%;
96
- }
97
- .kpop-buttons {
98
- flex-direction: column-reverse;
99
- text-align: center;
100
- }
101
- }
102
- .scrim {
103
- position: fixed;
104
- top: 0;
105
- bottom: 0;
106
- left: 0;
107
- right: 0;
108
- background: rgba(0, 0, 0, 0.6);
109
- z-index: -1;
110
- transition: opacity 0.2s ease-in-out, z-index 0.2s step-end;
111
- opacity: 0;
112
- }
113
-
114
- .scrim[data-scrim-open-value=true] {
115
- opacity: 1;
116
- transition: opacity 0.2s ease-in-out, z-index 0.2s step-start;
117
- }
@@ -1,72 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus";
2
- import ScrimController from "controllers/scrim_controller";
3
-
4
- export default class KpopController extends Controller {
5
- static targets = ["content", "closeButton"];
6
- static values = {
7
- open: Boolean,
8
- };
9
-
10
- contentTargetConnected(target) {
11
- // Set the modal content to temporary to ensure its omitted when caching the page
12
- target.setAttribute("data-turbo-temporary", "");
13
-
14
- // When switching modals a target may connect while scrim is already open
15
- if (this.openValue) return;
16
-
17
- if (ScrimController.showScrim({ dismiss: this.hasCloseButtonTarget })) {
18
- this.openValue = true;
19
- } else {
20
- this.#clear();
21
- }
22
- }
23
-
24
- contentTargetDisconnected() {
25
- // When switching modals there may still be content to show
26
- if (this.hasContentTarget) return;
27
-
28
- this.openValue = false;
29
- ScrimController.hideScrim();
30
- }
31
-
32
- openValueChanged(open) {
33
- this.element.style.display = open ? "flex" : "none";
34
- }
35
-
36
- dismiss() {
37
- if (!this.hasContentTarget || !this.openValue) return;
38
-
39
- const dismissUrl = this.contentTarget.dataset.dismissUrl;
40
- const dismissAction = this.contentTarget.dataset.dismissAction;
41
-
42
- if (dismissUrl) {
43
- if (dismissAction === "replace") {
44
- if (isSameUrl(document.referrer, dismissUrl)) {
45
- // if we came from the same page, send the user back
46
- history.back();
47
- } else {
48
- // if we came from a different page, dismiss the modal and replace url
49
- history.replaceState({}, "", dismissUrl);
50
- }
51
- } else {
52
- // default, send the user on to the specified URL
53
- window.location.href = dismissUrl;
54
- }
55
- }
56
-
57
- this.#clear();
58
- }
59
-
60
- #clear() {
61
- this.element.removeAttribute("src");
62
- this.element.innerHTML = "";
63
- }
64
- }
65
-
66
- function isSameUrl(previous, next) {
67
- try {
68
- return `${new URL(previous)}` === `${new URL(next, location.href)}`;
69
- } catch {
70
- return false;
71
- }
72
- }
@@ -1,9 +0,0 @@
1
- import KpopController from "../controllers/kpop_controller";
2
- import ScrimController from "../controllers/scrim_controller";
3
-
4
- const Definitions = [
5
- { identifier: "kpop", controllerConstructor: KpopController },
6
- { identifier: "scrim", controllerConstructor: ScrimController },
7
- ];
8
-
9
- export { Definitions as default, KpopController, ScrimController };
@@ -1,2 +0,0 @@
1
- @use "kpop";
2
- @use "scrim";
@@ -1,133 +0,0 @@
1
- $title-background-color: #344055 !default;
2
- $title-text-color: white !default;
3
- $min-width: 35rem !default;
4
- $max-width: 52rem !default;
5
- $min-height: 0 !default;
6
- $max-height: 80vh !default;
7
- $default-padding: 1rem 1.5rem !default;
8
-
9
- .kpop-container {
10
- display: none;
11
-
12
- position: fixed;
13
- left: 0;
14
- top: 0;
15
- right: 0;
16
- bottom: 0;
17
-
18
- justify-content: center;
19
- align-items: center;
20
- z-index: 1000;
21
- pointer-events: none;
22
-
23
- > * {
24
- pointer-events: auto;
25
- }
26
- }
27
-
28
- .kpop-modal {
29
- position: relative;
30
- overflow: hidden;
31
-
32
- display: grid;
33
- grid-template-areas:
34
- "title-bar"
35
- "header"
36
- "content"
37
- "footer";
38
- grid-template-rows: auto auto 1fr auto;
39
-
40
- border: 1px solid black;
41
- border-radius: 0.5rem;
42
- background-color: white;
43
-
44
- min-width: $min-width;
45
- max-width: $max-width;
46
- min-height: $min-height;
47
- max-height: $max-height;
48
-
49
- .kpop-title-bar {
50
- grid-area: title-bar;
51
- display: flex;
52
- background: $title-background-color;
53
- color: $title-text-color;
54
-
55
- .kpop-title {
56
- padding: $default-padding;
57
- flex-grow: 1;
58
- }
59
-
60
- .kpop-close {
61
- background: none;
62
- border: none;
63
- color: $title-text-color;
64
- cursor: pointer;
65
- display: block;
66
- font-size: 2rem;
67
- font-weight: bold;
68
- padding: 0 0.75rem;
69
- text-decoration: none;
70
- }
71
- }
72
-
73
- .kpop-header {
74
- grid-area: header;
75
- }
76
-
77
- .kpop-content {
78
- grid-area: content;
79
- display: flex;
80
- flex-direction: column;
81
- overflow: auto;
82
- }
83
-
84
- .kpop-footer {
85
- grid-area: footer;
86
- background: white;
87
- border-top: 1px solid black;
88
- padding: $default-padding;
89
- }
90
-
91
- .kpop-buttons {
92
- display: flex;
93
- gap: 0.5rem;
94
- justify-content: space-between;
95
- }
96
- }
97
-
98
- .kpop-modal.iframe {
99
- .kpop-content {
100
- overflow: unset;
101
- }
102
-
103
- iframe {
104
- height: $max-height;
105
- width: $max-width;
106
- flex-grow: 1;
107
- overflow: scroll;
108
- }
109
- }
110
-
111
- @media (max-width: 600px), (max-height: 600px) {
112
- .kpop-modal {
113
- max-width: unset;
114
- min-width: unset;
115
- width: 100%;
116
- height: 100%;
117
- max-height: 100vh;
118
- border-radius: 0;
119
- border: none;
120
- }
121
-
122
- .kpop-modal.iframe {
123
- iframe {
124
- width: 100%;
125
- height: 100%;
126
- }
127
- }
128
-
129
- .kpop-buttons {
130
- flex-direction: column-reverse;
131
- text-align: center;
132
- }
133
- }
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Rails/HelperInstanceVariable
4
- module Kpop
5
- class Modal
6
- delegate_missing_to :@context
7
-
8
- def initialize(context)
9
- @context = context
10
- end
11
-
12
- def render(options = {})
13
- dom_class = options.delete(:class)
14
-
15
- # Generate a title bar. This can be overridden by calling title_bar again.
16
- title_bar(options) unless options.fetch(:title, "").nil?
17
-
18
- # Render block. This may have side-effect writes to header/content/footer
19
- # etc. If @content is set then this value will be ignored.
20
- content = capture do
21
- yield self
22
- end
23
-
24
- tag.div(class: class_names("kpop-modal", dom_class),
25
- data: kpop_data_options(options),
26
- **options) do
27
- concat @title_bar
28
- concat @header if @header.present?
29
- concat @content.presence || tag.div(content, class: "kpop-content")
30
- concat @footer if @footer.present?
31
- end
32
- end
33
-
34
- # Generates a sticky title bar for the modal. Content should not be too long
35
- # as the bar does not provide wrapping.
36
- def title_bar(options = {}, &block)
37
- title = options.delete(:title)
38
- captive = options.delete(:captive)
39
- @title_bar = tag.div(class: "kpop-title-bar", **options) do
40
- concat(tag.span(class: "kpop-title") do
41
- concat(block ? (yield self) : title)
42
- end)
43
- concat(close_icon) unless captive
44
- end
45
- nil
46
- end
47
-
48
- # Generates sticky header content for the top of the modal. Content is not
49
- # padded, if you want padding you should provide a padding class.
50
- def header(**options, &block)
51
- modal_content(:header, **options, &block)
52
- end
53
-
54
- # Generates content for the modal. Content is not padded, if you want
55
- # padding you should provide a padding class.
56
- def content(**options, &block)
57
- modal_content(:content, **options, &block)
58
- end
59
-
60
- # Generates a sticky footer element at the bottom of the modal.
61
- # Footer is padded and contents are assumed to be buttons.
62
- def footer(**options, &block)
63
- modal_content(:footer, **options, &block)
64
- end
65
-
66
- def close_icon
67
- tag.button(
68
- "×",
69
- class: "kpop-close",
70
- data: {
71
- kpop_target: "closeButton",
72
- action: "click->kpop#dismiss:prevent",
73
- },
74
- )
75
- end
76
-
77
- private
78
-
79
- def kpop_data_options(options)
80
- data = options.delete(:data) || {}
81
- data.reverse_merge(
82
- kpop_target: "content",
83
- dismiss_action: options.delete(:dismiss_action),
84
- dismiss_url: options.delete(:dismiss_url),
85
- )
86
- end
87
-
88
- def class_for(name, options)
89
- class_names("kpop-#{name}", options.delete(:class))
90
- end
91
-
92
- def modal_content(name, **options, &block)
93
- instance_variable_set("@#{name}", tag.div(class: class_for(name, options), **options, &block))
94
- nil
95
- end
96
- end
97
- end
98
- # rubocop:enable Rails/HelperInstanceVariable
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ScrimHelper
4
- def scrim_tag(z_index: 40)
5
- tag.div(class: "scrim", data: { controller: "scrim", scrim_z_index_value: z_index, action: <<~ACTIONS })
6
- click->scrim#dismiss
7
- keyup@window->scrim#escape
8
- scrim:request:hide@window->scrim#hide
9
- scrim:request:show@window->scrim#show
10
- turbo:before-cache@document->scrim#hide
11
- ACTIONS
12
- end
13
- end