jet_ui 0.2.10 → 0.3.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 +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +30 -21
- data/app/assets/images/logo.svg +25 -0
- data/app/assets/javascripts/jet_ui/dialog_controller.js +95 -0
- data/app/assets/javascripts/jet_ui/dialogs_controller.js +248 -0
- data/app/assets/javascripts/jet_ui/drawer_controller.js +6 -0
- data/app/assets/javascripts/jet_ui/drawers_controller.js +5 -0
- data/app/assets/javascripts/jet_ui/modal_controller.js +6 -0
- data/app/assets/javascripts/jet_ui/modals_controller.js +5 -0
- data/app/assets/stylesheets/jet_ui/dialog.css +134 -0
- data/app/assets/stylesheets/jet_ui/theme.css +12 -0
- data/app/assets/stylesheets/jet_ui.css +1 -0
- data/app/components/jet_ui/dialog/body_component.rb +21 -0
- data/app/components/jet_ui/dialog/component.rb +86 -0
- data/app/components/jet_ui/dialog/footer_component.rb +45 -0
- data/app/components/jet_ui/dialog/header_component.rb +45 -0
- data/app/components/jet_ui/dialogs/component.rb +18 -0
- data/app/components/jet_ui/drawer/body_component.rb +2 -14
- data/app/components/jet_ui/drawer/component.rb +14 -44
- data/app/components/jet_ui/drawer/footer_component.rb +2 -38
- data/app/components/jet_ui/drawer/header_component.rb +6 -38
- data/app/components/jet_ui/flash/component.rb +2 -2
- data/app/components/jet_ui/icon/component.rb +1 -3
- data/app/components/jet_ui/modal/body_component.rb +2 -14
- data/app/components/jet_ui/modal/component.rb +14 -44
- data/app/components/jet_ui/modal/footer_component.rb +2 -38
- data/app/components/jet_ui/modal/header_component.rb +6 -38
- data/app/components/jet_ui/turbo_confirm/component.html.erb +13 -15
- data/lib/generators/jet_ui/eject/eject_generator.rb +19 -0
- data/lib/generators/jet_ui/install/install_generator.rb +3 -3
- data/lib/jet_ui/version.rb +1 -1
- metadata +33 -10
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
@layer theme, base, components, utilities;
|
|
2
|
+
|
|
3
|
+
@layer components {
|
|
4
|
+
/*
|
|
5
|
+
* .dialog is a pure, invisible positioning shell: fixed, sized, anchored to an edge (or
|
|
6
|
+
* centered). It must never carry a background, shadow, rounded corners, overflow-hidden, or
|
|
7
|
+
* — most importantly — transform/filter/perspective/contain/will-change, because any of
|
|
8
|
+
* those would make it a containing block for position: fixed descendants, breaking the
|
|
9
|
+
* flash portal (see dialogs_controller.js's #portalFlash — .dialog__flash-slot is a sibling
|
|
10
|
+
* of .dialog__panel for exactly this reason, not a descendant of it).
|
|
11
|
+
*
|
|
12
|
+
* All visible chrome (background, shadow, rounded corners, clipping) and the enter animation
|
|
13
|
+
* live on .dialog__panel instead, so the whole visible card animates as one piece instead of
|
|
14
|
+
* the chrome popping in instantly while only the content inside it slides/fades.
|
|
15
|
+
*/
|
|
16
|
+
.dialog {
|
|
17
|
+
@apply fixed hidden p-0 m-0 border-0 bg-transparent;
|
|
18
|
+
@apply animate-slide-up;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.dialog[open] {
|
|
22
|
+
@apply flex flex-col;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.dialog::backdrop {
|
|
26
|
+
@apply bg-overlay;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.dialog-center {
|
|
30
|
+
@apply inset-0 m-auto h-fit min-h-24 max-w-11/12 max-h-11/12;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.dialog-left {
|
|
34
|
+
@apply animate-slide-right;
|
|
35
|
+
@apply inset-y-0 left-0 right-auto h-full max-h-full;
|
|
36
|
+
max-width: min(90%, 1280px);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dialog-right {
|
|
40
|
+
@apply animate-slide-left;
|
|
41
|
+
@apply inset-y-0 left-auto right-0 h-full max-h-full;
|
|
42
|
+
max-width: min(90%, 1280px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.dialog-top {
|
|
46
|
+
@apply animate-slide-down;
|
|
47
|
+
@apply inset-x-0 bottom-auto top-0 w-full max-h-11/12 max-w-full;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dialog-bottom {
|
|
51
|
+
@apply animate-slide-up;
|
|
52
|
+
@apply inset-x-0 bottom-0 top-auto w-full max-h-11/12 max-w-full;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* A remote dialog's content arrives inside a <turbo-frame id="dialog">, which sits between
|
|
57
|
+
* .dialog and its children — make it transparent to layout so .dialog__panel and
|
|
58
|
+
* .dialog__flash-slot still behave as .dialog's direct flex children.
|
|
59
|
+
*/
|
|
60
|
+
.dialog > turbo-frame {
|
|
61
|
+
display: contents;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.dialog__panel {
|
|
65
|
+
@apply relative flex flex-col flex-1 overflow-hidden;
|
|
66
|
+
@apply shadow-sm bg-card rounded-modal;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dialog-left .dialog__panel {
|
|
70
|
+
@apply rounded-l-none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dialog-right .dialog__panel {
|
|
74
|
+
@apply rounded-r-none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dialog-top .dialog__panel {
|
|
78
|
+
@apply rounded-t-none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.dialog-bottom .dialog__panel {
|
|
82
|
+
@apply rounded-b-none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.dialog__header {
|
|
86
|
+
@apply flex items-start justify-between flex-shrink-0 p-4 px-6 backdrop-blur-sm bg-card/70;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.dialog__header-bordered {
|
|
90
|
+
@apply border-b border-border;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.dialog__title {
|
|
94
|
+
@apply text-xl font-semibold leading-tight text-foreground;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.dialog__subtitle {
|
|
98
|
+
@apply mt-1 text-sm text-muted-foreground;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.dialog__close {
|
|
102
|
+
@apply inline-flex items-center p-1 ml-auto text-sm transition-all duration-300 bg-transparent rounded-full cursor-pointer outline-0;
|
|
103
|
+
@apply text-muted-foreground hover:bg-accent hover:text-accent-foreground;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.dialog__body {
|
|
107
|
+
@apply flex-1 min-h-0 px-6 py-6 overflow-auto;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.dialog__footer {
|
|
111
|
+
@apply flex-shrink-0 w-full p-4 backdrop-blur-sm bg-card/70;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.dialog__footer-bordered {
|
|
115
|
+
@apply border-t border-border;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* Zero-size anchor the flash frame is portaled into (see dialogs_controller.js) so it is
|
|
120
|
+
* painted inside the topmost dialog's top-layer subtree. Deliberately a *sibling* of
|
|
121
|
+
* .dialog__panel, not nested inside it — .dialog__panel carries the enter animation
|
|
122
|
+
* (transform), which would otherwise turn it into a containing block for the portaled
|
|
123
|
+
* flash frame's position: fixed. display:contents keeps this slot itself out of the
|
|
124
|
+
* flex flow entirely.
|
|
125
|
+
*/
|
|
126
|
+
.dialog__flash-slot {
|
|
127
|
+
display: contents;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Direct (non-Turbo-Frame, non-dialog) page render — e.g. a dialog URL visited directly. */
|
|
131
|
+
.dialog-page {
|
|
132
|
+
@apply mt-4 mx-auto;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
from { opacity: 0.8; transform: translateX(3rem); }
|
|
14
14
|
to { opacity: 1; transform: translateX(0); }
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
@keyframes slideRight {
|
|
18
|
+
from { opacity: 0.8; transform: translateX(-3rem); }
|
|
19
|
+
to { opacity: 1; transform: translateX(0); }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes slideDown {
|
|
23
|
+
from { opacity: 0.7; transform: translateY(-2rem); }
|
|
24
|
+
to { opacity: 1; transform: translateY(0); }
|
|
25
|
+
}
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
:root {
|
|
@@ -33,6 +43,8 @@
|
|
|
33
43
|
--animate-fade-in: fadeIn 0.15s ease-in-out;
|
|
34
44
|
--animate-slide-up: slideUp 0.3s ease-in-out;
|
|
35
45
|
--animate-slide-left: slideLeft 0.3s ease-in-out;
|
|
46
|
+
--animate-slide-right: slideRight 0.3s ease-in-out;
|
|
47
|
+
--animate-slide-down: slideDown 0.3s ease-in-out;
|
|
36
48
|
|
|
37
49
|
/* Radius */
|
|
38
50
|
--radius-btn-xs: calc(var(--radius-md) * 0.75);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JetUi
|
|
4
|
+
module Dialog
|
|
5
|
+
class BodyComponent < JetUi::BaseComponent
|
|
6
|
+
def initialize(**options)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
content_tag :div, content, class: classes, **@options.except(:class)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def classes
|
|
17
|
+
class_names('dialog__body', @options[:class])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JetUi
|
|
4
|
+
module Dialog
|
|
5
|
+
class Component < JetUi::BaseComponent
|
|
6
|
+
SIZES = %w[sm md lg xl 2xl 3xl 4xl 5xl 6xl].freeze
|
|
7
|
+
DEFAULT_SIZE = '2xl'
|
|
8
|
+
|
|
9
|
+
POSITIONS = %i[center left right top bottom].freeze
|
|
10
|
+
DEFAULT_POSITION = :center
|
|
11
|
+
|
|
12
|
+
EDGE_POSITIONS = %i[left right top bottom].freeze
|
|
13
|
+
|
|
14
|
+
def initialize(title: nil, subtitle: nil, position: DEFAULT_POSITION, size: DEFAULT_SIZE,
|
|
15
|
+
id: nil, closable: true, dismissible: true, swipe: true)
|
|
16
|
+
@title = title
|
|
17
|
+
@subtitle = subtitle
|
|
18
|
+
@position = position.to_sym
|
|
19
|
+
@size = size.to_s
|
|
20
|
+
@id = id
|
|
21
|
+
@closable = closable
|
|
22
|
+
@dismissible = dismissible
|
|
23
|
+
@swipe = swipe && edge_position?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
erb_template <<~ERB
|
|
27
|
+
<%= container_tag do %>
|
|
28
|
+
<div class="dialog__panel" data-dialog-target="panel">
|
|
29
|
+
<%= helpers.jet_ui.dialog_header(title: @title, subtitle: @subtitle, closable: closable?) %>
|
|
30
|
+
<%= content %>
|
|
31
|
+
<div class="dialog__flash-slot"></div>
|
|
32
|
+
</div>
|
|
33
|
+
<% end %>
|
|
34
|
+
ERB
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def edge_position?
|
|
39
|
+
EDGE_POSITIONS.include?(@position)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def container_tag(&block)
|
|
43
|
+
# If @id is provided, we assume it's a sync dialog defined inline on the page.
|
|
44
|
+
return dialog_tag(id: @id, data: { dialogs_target: 'dialog' }, &block) if @id
|
|
45
|
+
|
|
46
|
+
# Async open (e.g. opened via a link with data: { turbo_frame: :dialog }).
|
|
47
|
+
# The <dialog> shell around this frame is created client-side by DialogsController
|
|
48
|
+
# before Turbo navigates the frame — see app/assets/javascripts/jet_ui/dialogs_controller.js.
|
|
49
|
+
return helpers.turbo_frame_tag(:dialog, &block) if helpers.turbo_frame_request?
|
|
50
|
+
|
|
51
|
+
# Not a Turbo Frame request (direct visit, new tab) — render as a plain page section.
|
|
52
|
+
content_tag :div, class: class_names('dialog-page', size_class), &block
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def dialog_tag(**options, &block)
|
|
56
|
+
content_tag :dialog, tabindex: '-1', class: dialog_classes, data: dialog_data(options.delete(:data)),
|
|
57
|
+
**options, &block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dialog_classes
|
|
61
|
+
class_names('dialog', "dialog-#{@position}", size_class)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def dialog_data(extra)
|
|
65
|
+
{
|
|
66
|
+
controller: 'dialog',
|
|
67
|
+
dialog_position_value: @position,
|
|
68
|
+
dialog_dismissible_value: @dismissible,
|
|
69
|
+
dialog_swipe_value: @swipe
|
|
70
|
+
}.merge(extra || {})
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def size_class
|
|
74
|
+
horizontal_edge? ? "h-#{@size}" : "w-#{@size}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def horizontal_edge?
|
|
78
|
+
%i[top bottom].include?(@position)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def closable?
|
|
82
|
+
@closable && (helpers.turbo_frame_request? || @id)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JetUi
|
|
4
|
+
module Dialog
|
|
5
|
+
class FooterComponent < JetUi::BaseComponent
|
|
6
|
+
def initialize(direction: :row, align: :start, justify: :start, bordered: true, **options)
|
|
7
|
+
@direction = direction
|
|
8
|
+
@align = align
|
|
9
|
+
@justify = justify
|
|
10
|
+
@bordered = bordered
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
content_tag :div, content, class: classes, **@options.except(:class)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def classes
|
|
21
|
+
class_names(
|
|
22
|
+
'dialog__footer',
|
|
23
|
+
{ 'dialog__footer-bordered' => @bordered },
|
|
24
|
+
direction_class,
|
|
25
|
+
align_class,
|
|
26
|
+
justify_class,
|
|
27
|
+
@options[:class]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def direction_class
|
|
32
|
+
{ col: 'flex flex-col gap-2', row: 'flex flex-row gap-2' }[@direction]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def align_class
|
|
36
|
+
{ start: 'items-start', center: 'items-center', end: 'items-end' }[@align]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def justify_class
|
|
40
|
+
{ start: 'justify-start', center: 'justify-center', end: 'justify-end',
|
|
41
|
+
between: 'justify-between' }[@justify]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JetUi
|
|
4
|
+
module Dialog
|
|
5
|
+
class HeaderComponent < JetUi::BaseComponent
|
|
6
|
+
def initialize(title: nil, subtitle: nil, closable: true, bordered: true, **options)
|
|
7
|
+
@title = title
|
|
8
|
+
@subtitle = subtitle
|
|
9
|
+
@closable = closable
|
|
10
|
+
@bordered = bordered
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
erb_template <<~ERB
|
|
15
|
+
<div class="<%= classes %>">
|
|
16
|
+
<div>
|
|
17
|
+
<% if @title %>
|
|
18
|
+
<h3 class="dialog__title"><%= @title %></h3>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% if @subtitle %>
|
|
21
|
+
<div class="dialog__subtitle"><%= @subtitle %></div>
|
|
22
|
+
<% end %>
|
|
23
|
+
<%= content %>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<% if @closable %>
|
|
27
|
+
<button type="button" class="dialog__close" data-action="click->dialog#close" aria-label="Close">
|
|
28
|
+
<%= helpers.jet_ui.icon('x-mark', size: 6) %>
|
|
29
|
+
</button>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
ERB
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def classes
|
|
37
|
+
class_names(
|
|
38
|
+
'dialog__header',
|
|
39
|
+
{ 'dialog__header-bordered' => @bordered },
|
|
40
|
+
@options[:class]
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JetUi
|
|
4
|
+
module Dialogs
|
|
5
|
+
# Mount point for the dialog stack. Renders the `#dialogs` root that DialogsController
|
|
6
|
+
# manages (see app/assets/javascripts/jet_ui/dialogs_controller.js) plus the sentinel
|
|
7
|
+
# <turbo-frame id="dialog"> that every async dialog open (data: { turbo_frame: :dialog })
|
|
8
|
+
# adopts into a freshly built <dialog> shell before Turbo navigates it. Render this once
|
|
9
|
+
# in the host application's layout.
|
|
10
|
+
class Component < JetUi::BaseComponent
|
|
11
|
+
erb_template <<~ERB
|
|
12
|
+
<div id="dialogs" data-dialogs-target="root" data-turbo-permanent data-turbo-cache="false">
|
|
13
|
+
<%= helpers.turbo_frame_tag :dialog, data: { dialogs_target: 'sentinel' } %>
|
|
14
|
+
</div>
|
|
15
|
+
ERB
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module JetUi
|
|
4
4
|
module Drawer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@options = options
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call
|
|
11
|
-
content_tag :div, content, class: classes, **@options.except(:class)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def classes
|
|
17
|
-
class_names('drawer__body', @options[:class])
|
|
18
|
-
end
|
|
5
|
+
# @deprecated Use {JetUi::Dialog::BodyComponent} instead.
|
|
6
|
+
class BodyComponent < JetUi::Dialog::BodyComponent
|
|
19
7
|
end
|
|
20
8
|
end
|
|
21
9
|
end
|
|
@@ -2,52 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module JetUi
|
|
4
4
|
module Drawer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@
|
|
12
|
-
@size = size
|
|
13
|
-
@id = id
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
erb_template <<~ERB
|
|
17
|
-
<%= container_tag do %>
|
|
18
|
-
<%= helpers.jet_ui.drawer_header(title: @title, subtitle: @subtitle, closable: closable?, id: @id) %>
|
|
19
|
-
<%= content %>
|
|
20
|
-
<% end %>
|
|
21
|
-
ERB
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def container_tag(&block)
|
|
26
|
-
return sync_dialog(&block) if @id
|
|
27
|
-
return turbo_frame_dialog(&block) if helpers.turbo_frame_request?
|
|
28
|
-
|
|
29
|
-
content_tag :div, class: class_names('mx-auto bg-background', "w-#{@size}"), &block
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def sync_dialog(&block)
|
|
33
|
-
dialog_tag(id: @id, data: { drawers_target: 'dialog' }, &block)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def turbo_frame_dialog(&block)
|
|
37
|
-
helpers.turbo_frame_tag :drawer do
|
|
38
|
-
dialog_tag(id: :drawerDialog, data: { controller: 'drawer' }) do
|
|
39
|
-
helpers.turbo_frame_tag(:drawerContent, &block)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def dialog_tag(**options, &block)
|
|
45
|
-
attrs = { tabindex: '-1', class: class_names('drawer', 'animate-slide-left', "w-#{@size}") }
|
|
46
|
-
content_tag :dialog, **attrs, **options, &block
|
|
5
|
+
# @deprecated Use {JetUi::Dialog::Component} with `position: :right` instead.
|
|
6
|
+
# Kept as a thin shim so existing `jet_ui.drawer(...)` calls and
|
|
7
|
+
# `data: { turbo_frame: :drawer }` links keep working. Will be removed in the next
|
|
8
|
+
# major version.
|
|
9
|
+
class Component < JetUi::Dialog::Component
|
|
10
|
+
def self.deprecator
|
|
11
|
+
@deprecator ||= ActiveSupport::Deprecation.new('1.0', 'JetUi')
|
|
47
12
|
end
|
|
48
13
|
|
|
49
|
-
def
|
|
50
|
-
|
|
14
|
+
def initialize(title: nil, subtitle: nil, size: JetUi::Dialog::Component::DEFAULT_SIZE, id: nil)
|
|
15
|
+
self.class.deprecator.warn(
|
|
16
|
+
'JetUi::Drawer::Component is deprecated and will be removed in the next major ' \
|
|
17
|
+
'version. Use JetUi::Dialog::Component with position: :right instead ' \
|
|
18
|
+
'(jet_ui.dialog).'
|
|
19
|
+
)
|
|
20
|
+
super(title: title, subtitle: subtitle, position: :right, size: size, id: id)
|
|
51
21
|
end
|
|
52
22
|
end
|
|
53
23
|
end
|
|
@@ -2,44 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module JetUi
|
|
4
4
|
module Drawer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@direction = direction
|
|
8
|
-
@align = align
|
|
9
|
-
@justify = justify
|
|
10
|
-
@bordered = bordered
|
|
11
|
-
@options = options
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def call
|
|
15
|
-
content_tag :div, content, class: classes, **@options.except(:class)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def classes
|
|
21
|
-
class_names(
|
|
22
|
-
'drawer__footer',
|
|
23
|
-
{ 'drawer__footer-bordered' => @bordered },
|
|
24
|
-
direction_class,
|
|
25
|
-
align_class,
|
|
26
|
-
justify_class,
|
|
27
|
-
@options[:class]
|
|
28
|
-
)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def direction_class
|
|
32
|
-
{ col: 'flex flex-col gap-2', row: 'flex flex-row gap-2' }[@direction]
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def align_class
|
|
36
|
-
{ start: 'items-start', center: 'items-center', end: 'items-end' }[@align]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def justify_class
|
|
40
|
-
{ start: 'justify-start', center: 'justify-center', end: 'justify-end',
|
|
41
|
-
between: 'justify-between' }[@justify]
|
|
42
|
-
end
|
|
5
|
+
# @deprecated Use {JetUi::Dialog::FooterComponent} instead.
|
|
6
|
+
class FooterComponent < JetUi::Dialog::FooterComponent
|
|
43
7
|
end
|
|
44
8
|
end
|
|
45
9
|
end
|
|
@@ -2,44 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module JetUi
|
|
4
4
|
module Drawer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@bordered = bordered
|
|
12
|
-
@options = options
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
erb_template <<~ERB
|
|
16
|
-
<div class="<%= classes %>">
|
|
17
|
-
<div>
|
|
18
|
-
<% if @title %>
|
|
19
|
-
<h3 class="drawer__title"><%= @title %></h3>
|
|
20
|
-
<% end %>
|
|
21
|
-
<% if @subtitle %>
|
|
22
|
-
<div class="drawer__subtitle"><%= @subtitle %></div>
|
|
23
|
-
<% end %>
|
|
24
|
-
<%= content %>
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
<% if @closable %>
|
|
28
|
-
<button type="button" class="drawer__close" data-action="click->drawer#close click->drawers#close" aria-label="Close" data-id="<%= @id %>">
|
|
29
|
-
<%= helpers.jet_ui.icon("x-mark", size: 6) %>
|
|
30
|
-
</button>
|
|
31
|
-
<% end %>
|
|
32
|
-
</div>
|
|
33
|
-
ERB
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def classes
|
|
38
|
-
class_names(
|
|
39
|
-
'drawer__header',
|
|
40
|
-
{ 'drawer__header-bordered' => @bordered },
|
|
41
|
-
@options[:class]
|
|
42
|
-
)
|
|
5
|
+
# @deprecated Use {JetUi::Dialog::HeaderComponent} instead.
|
|
6
|
+
class HeaderComponent < JetUi::Dialog::HeaderComponent
|
|
7
|
+
# `id:` accepted and ignored for signature compatibility with the pre-deprecation API.
|
|
8
|
+
def initialize(title: nil, subtitle: nil, closable: true, id: nil, bordered: true, # rubocop:disable Lint/UnusedMethodArgument
|
|
9
|
+
**options)
|
|
10
|
+
super(title: title, subtitle: subtitle, closable: closable, bordered: bordered, **options)
|
|
43
11
|
end
|
|
44
12
|
end
|
|
45
13
|
end
|
|
@@ -34,8 +34,8 @@ module JetUi
|
|
|
34
34
|
def item_data
|
|
35
35
|
attrs = { controller: 'flash', action: 'turbo:morph@window->flash#connect' }
|
|
36
36
|
if @dismissible
|
|
37
|
-
attrs[:
|
|
38
|
-
attrs[:
|
|
37
|
+
attrs[:'flash-dismiss-after-value'] = DISMISS_AFTER
|
|
38
|
+
attrs[:'flash-show-delay-value'] = SHOW_DELAY
|
|
39
39
|
end
|
|
40
40
|
attrs
|
|
41
41
|
end
|
|
@@ -16,9 +16,7 @@ module JetUi
|
|
|
16
16
|
def call
|
|
17
17
|
icon_name = @name.presence || content.to_s.strip
|
|
18
18
|
svg = File.read(ICONS_PATH.join("#{icon_name}.svg"))
|
|
19
|
-
# rubocop:disable Rails/OutputSafety
|
|
20
|
-
svg.sub('<svg', "<svg #{html_attributes(icon_name)}").html_safe
|
|
21
|
-
# rubocop:enable Rails/OutputSafety
|
|
19
|
+
svg.sub('<svg', "<svg #{html_attributes(icon_name)}").html_safe # rubocop:disable Rails/OutputSafety
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
private
|
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module JetUi
|
|
4
4
|
module Modal
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@options = options
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call
|
|
11
|
-
content_tag :div, content, class: classes, **@options.except(:class)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def classes
|
|
17
|
-
class_names('modal__body', @options[:class])
|
|
18
|
-
end
|
|
5
|
+
# @deprecated Use {JetUi::Dialog::BodyComponent} instead.
|
|
6
|
+
class BodyComponent < JetUi::Dialog::BodyComponent
|
|
19
7
|
end
|
|
20
8
|
end
|
|
21
9
|
end
|