cafe_car 0.1.1 → 0.1.2
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/README.md +155 -40
- data/Rakefile +9 -1
- data/app/assets/fonts/Lexend.css +7 -0
- data/app/assets/fonts/Lexend.ttf +0 -0
- data/app/assets/stylesheets/cafe_car/themes/defaults.css +58 -59
- data/app/assets/stylesheets/cafe_car/tooltips.css +20 -0
- data/app/assets/stylesheets/cafe_car/utility.css +1 -2
- data/app/assets/stylesheets/cafe_car.css +17 -6
- data/app/assets/stylesheets/ui/Alert.css +2 -1
- data/app/assets/stylesheets/ui/Button.css +6 -6
- data/app/assets/stylesheets/ui/Card.css +7 -3
- data/app/assets/stylesheets/ui/Chat.css +33 -0
- data/app/assets/stylesheets/ui/Close.css +11 -0
- data/app/assets/stylesheets/ui/Grid.css +2 -0
- data/app/assets/stylesheets/ui/Icon.css +3 -3
- data/app/assets/stylesheets/ui/Layout.css +20 -13
- data/app/assets/stylesheets/ui/Modal.css +5 -12
- data/app/assets/stylesheets/ui/Navigation.css +13 -5
- data/app/assets/stylesheets/ui/Page.css +42 -3
- data/app/assets/stylesheets/ui/Table.css +27 -56
- data/app/assets/stylesheets/ui/components.css +2 -0
- data/app/controllers/cafe_car/examples_controller.rb +1 -1
- data/app/controllers/cafe_car/sessions_controller.rb +30 -0
- data/app/controllers/concerns/cafe_car/authentication.rb +61 -0
- data/app/javascript/cafe_car.js +16 -11
- data/app/models/cafe_car/session.rb +18 -0
- data/app/policies/cafe_car/session_policy.rb +19 -0
- data/app/presenters/cafe_car/active_storage/attachment_presenter.rb +5 -4
- data/app/presenters/cafe_car/code_presenter.rb +18 -0
- data/app/presenters/cafe_car/date_presenter.rb +1 -0
- data/app/presenters/cafe_car/date_time_presenter.rb +2 -2
- data/app/presenters/cafe_car/enumerable_presenter.rb +1 -1
- data/app/presenters/cafe_car/hash_presenter.rb +3 -8
- data/app/presenters/cafe_car/presenter.rb +22 -12
- data/app/presenters/cafe_car/string_presenter.rb +2 -2
- data/app/ui/cafe_car/ui/button.rb +2 -1
- data/app/ui/cafe_car/ui/card.rb +18 -0
- data/app/ui/cafe_car/ui/grid.rb +30 -0
- data/app/ui/cafe_car/ui/layout.rb +7 -0
- data/app/ui/cafe_car/ui/page.rb +5 -1
- data/app/views/application/_body.html.haml +2 -1
- data/app/views/application/_controls.html.haml +1 -0
- data/app/views/application/_debug.html.haml +9 -2
- data/app/views/application/_errors.html.haml +4 -8
- data/app/views/application/_grid.html.haml +1 -1
- data/app/views/application/_grid_item.html.haml +1 -1
- data/app/views/application/_head.html.haml +1 -0
- data/app/views/application/_index.html.haml +6 -2
- data/app/views/application/_index_actions.html.haml +3 -3
- data/app/views/application/_navigation.html.haml +7 -0
- data/app/views/application/_navigation_links.html.haml +1 -1
- data/app/views/application/_notes.html.haml +1 -0
- data/app/views/application/_popup.html.haml +7 -0
- data/app/views/cafe_car/application/edit.html.haml +1 -1
- data/app/views/cafe_car/application/edit.turbo_stream.haml +3 -5
- data/app/views/cafe_car/application/index.html.haml +3 -0
- data/app/views/cafe_car/application/new.turbo_stream.haml +5 -6
- data/app/views/cafe_car/application/show.html.haml +2 -2
- data/app/views/cafe_car/examples/ui/_chat.html.haml +3 -0
- data/app/views/cafe_car/examples/ui/_info_circle.html.haml +1 -1
- data/app/views/cafe_car/examples/ui/_modal.html.haml +1 -1
- data/app/views/passwords_mailer/reset.html.haml +5 -0
- data/app/views/passwords_mailer/reset.text.erb +4 -0
- data/app/views/ui/_card.html.haml +6 -11
- data/app/views/ui/_field.html.haml +1 -7
- data/app/views/ui/_modal_close.html.haml +1 -2
- data/app/views/ui/_page.html.haml +6 -12
- data/config/brakeman.ignore +3 -3
- data/config/locales/en.yml +10 -2
- data/config/routes.rb +5 -1
- data/db/migrate/20251005220017_create_slugs.rb +2 -2
- data/lib/cafe_car/active_record.rb +1 -1
- data/lib/cafe_car/application_responder.rb +6 -0
- data/lib/cafe_car/attributes.rb +1 -1
- data/lib/cafe_car/auto_resolver.rb +1 -1
- data/lib/cafe_car/component.rb +102 -39
- data/lib/cafe_car/context.rb +5 -4
- data/lib/cafe_car/controller/filtering.rb +9 -1
- data/lib/cafe_car/controller.rb +52 -13
- data/lib/cafe_car/core_ext/array.rb +13 -0
- data/lib/cafe_car/core_ext/hash.rb +15 -0
- data/lib/cafe_car/core_ext/module.rb +15 -0
- data/lib/cafe_car/core_ext.rb +0 -2
- data/lib/cafe_car/current.rb +4 -1
- data/lib/cafe_car/engine.rb +9 -2
- data/lib/cafe_car/field_builder.rb +1 -1
- data/lib/cafe_car/field_info.rb +14 -12
- data/lib/cafe_car/fields.rb +7 -0
- data/lib/cafe_car/filter/field_info.rb +1 -1
- data/lib/cafe_car/filter/form_builder.rb +2 -2
- data/lib/cafe_car/filter_builder.rb +1 -1
- data/lib/cafe_car/form_builder.rb +1 -1
- data/lib/cafe_car/generators.rb +1 -1
- data/lib/cafe_car/helpers.rb +37 -10
- data/lib/cafe_car/href_builder.rb +35 -9
- data/lib/cafe_car/input_builder.rb +1 -1
- data/lib/cafe_car/link_builder.rb +14 -11
- data/lib/cafe_car/model.rb +2 -2
- data/lib/cafe_car/navigation.rb +10 -10
- data/lib/cafe_car/option_helpers.rb +11 -5
- data/lib/cafe_car/param_parser.rb +10 -6
- data/lib/cafe_car/policy.rb +2 -2
- data/lib/cafe_car/query_builder.rb +3 -3
- data/lib/cafe_car/resolver.rb +5 -1
- data/lib/cafe_car/routing.rb +1 -1
- data/lib/cafe_car/table/builder.rb +3 -2
- data/lib/cafe_car/table/head_builder.rb +2 -2
- data/lib/cafe_car/table/label_builder.rb +1 -1
- data/lib/cafe_car/table/row_builder.rb +5 -7
- data/lib/cafe_car/table_builder.rb +3 -3
- data/lib/cafe_car/ui.rb +2 -0
- data/lib/cafe_car/version.rb +1 -1
- data/lib/cafe_car/visitors.rb +2 -2
- data/lib/cafe_car.rb +25 -0
- data/lib/generators/cafe_car/controller/templates/controller.rb.tt +1 -1
- data/lib/generators/cafe_car/install/install_generator.rb +0 -1
- data/lib/generators/cafe_car/sessions/USAGE +17 -0
- data/lib/generators/cafe_car/sessions/sessions_generator.rb +29 -0
- data/lib/generators/cafe_car/sessions/templates/create_sessions.rb.tt +12 -0
- data/lib/tasks/holdco_tasks.rake +532 -0
- data/lib/tasks/templates/tasks_header.md +37 -0
- metadata +76 -48
- data/app/models/cafe_car/slug.rb +0 -3
- data/app/views/ui/_grid.html.haml +0 -17
- data/app/views/ui/_layout_menu.html.haml +0 -2
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
.Button {
|
|
2
2
|
--background: var(--button);
|
|
3
3
|
--color: var(--color);
|
|
4
|
+
--darken: rgb(0 0 0 / 0.2);
|
|
5
|
+
--lighten: rgb(255 255 255 / 0.2);
|
|
4
6
|
background: var(--background);
|
|
5
7
|
color: var(--color);
|
|
6
8
|
border-radius: var(--control-radius);
|
|
7
9
|
padding: var(--half-gap);
|
|
8
|
-
box-shadow:
|
|
9
|
-
/* border: 1px solid rgba(255,255,255, 0.3); */
|
|
10
|
+
box-shadow: inset var(--darken) 0 -1px 0 1px;
|
|
10
11
|
border: 0;
|
|
11
12
|
display: flex;
|
|
12
13
|
align-items: center;
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
.Button:where(:not(.Button-current)):hover {
|
|
18
19
|
cursor: pointer;
|
|
19
20
|
text-decoration: none;
|
|
20
|
-
box-shadow:
|
|
21
|
+
box-shadow: inset var(--darken) 0 -2px 0 1px;
|
|
21
22
|
filter: brightness(1.05);
|
|
22
23
|
z-index: 1;
|
|
23
24
|
}
|
|
@@ -25,10 +26,9 @@
|
|
|
25
26
|
.Button:active,
|
|
26
27
|
.Button-current {
|
|
27
28
|
text-decoration: none;
|
|
28
|
-
box-shadow: rgba(34, 32, 29, 0.1) 0px 1px 2px;
|
|
29
29
|
filter: brightness(0.95);
|
|
30
|
-
|
|
31
|
-
box-shadow: inset
|
|
30
|
+
border-bottom-width: 1px;
|
|
31
|
+
box-shadow: inset var(--darken) 0 1px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.Button-primary {
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
.Card_Image {
|
|
9
9
|
display: block;
|
|
10
10
|
aspect-ratio: 5/4;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
object-position: center;
|
|
12
|
+
object-fit: cover;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
width: 100%;
|
|
13
15
|
border-top-left-radius: var(--radius);
|
|
14
16
|
border-top-right-radius: var(--radius);
|
|
15
17
|
/* &::before {
|
|
@@ -43,7 +45,9 @@
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
:is(.Card_Head, .Card_Section, .Card_Foot) {
|
|
46
|
-
& + &,
|
|
48
|
+
& + &,
|
|
49
|
+
& + form > &,
|
|
50
|
+
form > & + & {
|
|
47
51
|
border-top: 1px solid var(--border);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.Chat {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--gap);
|
|
5
|
+
padding: var(--gap);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.Chat_Message {
|
|
9
|
+
padding: var(--gap);
|
|
10
|
+
border-radius: var(--radius);
|
|
11
|
+
box-shadow: var(--box-shadow);
|
|
12
|
+
max-width: 80%;
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.Chat_Message-local,
|
|
17
|
+
.Chat_Message-right {
|
|
18
|
+
align-self: end;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.Chat_Message-remote,
|
|
22
|
+
.Chat_Message-left {
|
|
23
|
+
align-self: start;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.Chat_Message-local {
|
|
27
|
+
background-color: var(--blue);
|
|
28
|
+
color: var(--white);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.Chat_Message-remote {
|
|
32
|
+
background-color: var(--gray);
|
|
33
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
@charset "utf-8";
|
|
2
2
|
|
|
3
3
|
.Icon {
|
|
4
|
-
vertical-align: bottom;
|
|
5
|
-
display: flex;
|
|
4
|
+
vertical-align: text-bottom;
|
|
5
|
+
display: inline-flex;
|
|
6
6
|
gap: var(--half-gap);
|
|
7
7
|
font-style: inherit;
|
|
8
8
|
|
|
9
9
|
&::before {
|
|
10
10
|
display: block;
|
|
11
|
-
content:
|
|
11
|
+
content: "";
|
|
12
12
|
height: 1.5em;
|
|
13
13
|
width: 1.5em;
|
|
14
14
|
}
|
|
@@ -3,35 +3,44 @@
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
.Layout {
|
|
6
|
-
display: flex;
|
|
7
|
-
align-items:
|
|
6
|
+
/* display: flex; */
|
|
7
|
+
/* align-items: stretch; */
|
|
8
|
+
flex-grow: 1;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
.Layout_Sidebar {
|
|
11
|
-
--indent: .7rem;
|
|
12
|
+
--indent: 0.7rem;
|
|
12
13
|
|
|
13
14
|
width: var(--sidebar-width);
|
|
14
|
-
position: sticky;
|
|
15
|
+
/* position: sticky; */
|
|
16
|
+
position: fixed;
|
|
15
17
|
inset: 0;
|
|
18
|
+
|
|
16
19
|
overflow: auto;
|
|
20
|
+
max-height: 100dvh;
|
|
21
|
+
height: 100%;
|
|
17
22
|
|
|
18
23
|
display: flex;
|
|
19
24
|
flex-flow: column;
|
|
20
25
|
flex-shrink: 0;
|
|
26
|
+
align-self: start;
|
|
21
27
|
|
|
22
28
|
padding: var(--gap);
|
|
23
29
|
white-space: nowrap;
|
|
24
|
-
transition:
|
|
30
|
+
transition:
|
|
31
|
+
margin var(--duration),
|
|
32
|
+
width var(--duration);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
.Layout_Main {
|
|
28
36
|
transition: transform var(--duration);
|
|
29
|
-
|
|
37
|
+
margin-left: var(--sidebar-width);
|
|
30
38
|
display: flex;
|
|
31
39
|
flex-flow: column;
|
|
32
40
|
align-items: stretch;
|
|
33
41
|
flex-grow: 1;
|
|
34
42
|
padding: var(--gap);
|
|
43
|
+
position: relative;
|
|
35
44
|
z-index: 1;
|
|
36
45
|
}
|
|
37
46
|
|
|
@@ -43,7 +52,6 @@
|
|
|
43
52
|
height: 40px;
|
|
44
53
|
text-align: center;
|
|
45
54
|
cursor: pointer;
|
|
46
|
-
vertical-align: center;
|
|
47
55
|
border-color: transparent;
|
|
48
56
|
background: none;
|
|
49
57
|
|
|
@@ -62,7 +70,7 @@
|
|
|
62
70
|
display: block;
|
|
63
71
|
font-size: 30px;
|
|
64
72
|
line-height: 1;
|
|
65
|
-
margin-top: -
|
|
73
|
+
margin-top: -1px;
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -72,16 +80,15 @@
|
|
|
72
80
|
transform: translateX(var(--sidebar-width));
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
.Layout_Sidebar {
|
|
84
|
+
margin-left: calc(-1 * var(--sidebar-width));
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
body:has(.Layout_Menu:checked) {
|
|
76
88
|
overflow-x: hidden;
|
|
77
89
|
}
|
|
78
90
|
|
|
79
91
|
body:not(:has(.Layout_Menu:checked)) {
|
|
80
|
-
.Layout_Sidebar {
|
|
81
|
-
margin-left: calc(-1 * var(--sidebar-width));
|
|
82
|
-
/* width: 100vw; */
|
|
83
|
-
}
|
|
84
|
-
|
|
85
92
|
.Layout_Main {
|
|
86
93
|
transform: none;
|
|
87
94
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.Modal {
|
|
2
|
-
z-index:
|
|
2
|
+
z-index: 9;
|
|
3
3
|
inset: 0;
|
|
4
4
|
display: grid;
|
|
5
5
|
grid-template-columns: fit-content(var(--modal-width));
|
|
@@ -10,18 +10,11 @@
|
|
|
10
10
|
overflow: auto;
|
|
11
11
|
backdrop-filter: blur(8px);
|
|
12
12
|
padding: var(--gap);
|
|
13
|
-
animation: fade-in
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.Modal_Close {
|
|
17
|
-
font-size: 150%;
|
|
18
|
-
cursor: pointer;
|
|
19
|
-
line-height: 1;
|
|
20
|
-
}
|
|
13
|
+
animation: fade-in var(--duration);
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
& > * {
|
|
16
|
+
min-width: 250px;
|
|
17
|
+
}
|
|
25
18
|
}
|
|
26
19
|
|
|
27
20
|
.Modal-fixed {
|
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
.Navigation {
|
|
2
|
-
--indent: .7em;
|
|
3
|
-
display:
|
|
2
|
+
--indent: 0.7em;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-flow: column;
|
|
5
|
+
flex-grow: 1;
|
|
4
6
|
align-content: start;
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
.Navigation_Link {
|
|
8
|
-
padding: .7em;
|
|
10
|
+
padding: 0.7em;
|
|
9
11
|
position: relative;
|
|
10
12
|
color: inherit;
|
|
11
13
|
display: flex;
|
|
12
14
|
align-items: center;
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
.Navigation_Link-bottom {
|
|
18
|
+
margin-top: auto;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
.Navigation_Link:is(a):hover,
|
|
17
|
-
.
|
|
22
|
+
.Navigation_Link-current,
|
|
23
|
+
.Navigation_Link-ancestor,
|
|
18
24
|
.Navigation_Link.current {
|
|
19
25
|
text-decoration: none;
|
|
26
|
+
|
|
20
27
|
&::after {
|
|
21
|
-
content:
|
|
28
|
+
content: "";
|
|
22
29
|
display: block;
|
|
23
30
|
position: absolute;
|
|
24
31
|
inset: 3px 0;
|
|
25
32
|
border-radius: var(--control-radius);
|
|
33
|
+
box-shadow: inset rgb(0 0 0 / 0.2) 0 -2px;
|
|
26
34
|
background: var(--hover);
|
|
27
35
|
z-index: -1;
|
|
28
36
|
}
|
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
container: Page;
|
|
3
3
|
display: flex;
|
|
4
4
|
flex-flow: wrap;
|
|
5
|
-
justify-content:
|
|
5
|
+
justify-content: start;
|
|
6
6
|
gap: var(--gap);
|
|
7
7
|
background: var(--background);
|
|
8
8
|
border-radius: var(--radius);
|
|
9
|
+
min-height: 100%;
|
|
10
|
+
align-content: start;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.Page-center {
|
|
14
|
+
justify-content: start;
|
|
15
|
+
padding: var(--gap);
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
.Page_Head {
|
|
@@ -18,6 +25,10 @@
|
|
|
18
25
|
left: var(--gap);
|
|
19
26
|
}
|
|
20
27
|
|
|
28
|
+
.Page_Foot {
|
|
29
|
+
width: 100%;
|
|
30
|
+
}
|
|
31
|
+
|
|
21
32
|
.Page_Title {
|
|
22
33
|
margin: 0;
|
|
23
34
|
}
|
|
@@ -28,21 +39,48 @@
|
|
|
28
39
|
margin-left: auto;
|
|
29
40
|
}
|
|
30
41
|
|
|
42
|
+
@keyframes Page_Aside-popup {
|
|
43
|
+
from {
|
|
44
|
+
margin-right: -400px;
|
|
45
|
+
opacity: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
to {
|
|
49
|
+
margin-right: 0px;
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
31
54
|
.Page_Aside {
|
|
32
55
|
min-width: 300px;
|
|
56
|
+
max-width: 720px;
|
|
33
57
|
display: flex;
|
|
34
58
|
flex-flow: column;
|
|
35
59
|
gap: var(--gap);
|
|
60
|
+
|
|
61
|
+
&.popup {
|
|
62
|
+
animation: Page_Aside-popup var(--duration);
|
|
63
|
+
z-index: 2;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&.remove {
|
|
67
|
+
opacity: 1;
|
|
68
|
+
margin-right: -400px;
|
|
69
|
+
pointer-events: none;
|
|
70
|
+
}
|
|
36
71
|
}
|
|
37
72
|
|
|
38
73
|
.Page_Body {
|
|
39
|
-
flex: 1 1
|
|
74
|
+
flex: 1 1;
|
|
75
|
+
width: 0;
|
|
40
76
|
display: flex;
|
|
41
77
|
flex-flow: column;
|
|
42
78
|
gap: var(--gap);
|
|
79
|
+
/* overflow: auto; */
|
|
43
80
|
}
|
|
44
81
|
|
|
45
|
-
.Page-slim .Page_Body
|
|
82
|
+
.Page-slim .Page_Body,
|
|
83
|
+
.Page_Body-slim {
|
|
46
84
|
max-width: 720px;
|
|
47
85
|
}
|
|
48
86
|
|
|
@@ -62,5 +100,6 @@
|
|
|
62
100
|
.Page_Aside,
|
|
63
101
|
.Page_Foot {
|
|
64
102
|
justify-self: stretch;
|
|
103
|
+
width: auto;
|
|
65
104
|
}
|
|
66
105
|
}
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
align-items: stretch;
|
|
5
5
|
box-shadow: var(--box-shadow);
|
|
6
6
|
border-radius: var(--radius);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
position: relative;
|
|
10
|
-
z-index: 0;
|
|
7
|
+
background: var(--card);
|
|
8
|
+
min-width: min-content;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
.Table_Head,
|
|
@@ -19,60 +17,29 @@
|
|
|
19
17
|
grid-column: 1/-1;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
.Table_Body {
|
|
23
|
-
background: var(--card);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
20
|
.Table_Head {
|
|
27
|
-
/*overflow: hidden;*/
|
|
28
21
|
z-index: 2;
|
|
29
22
|
white-space: nowrap;
|
|
30
23
|
font-weight: bold;
|
|
31
24
|
position: relative;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
position: absolute;
|
|
37
|
-
inset: 0;
|
|
38
|
-
height: 200%;
|
|
39
|
-
pointer-events: none;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
&::before {
|
|
43
|
-
backdrop-filter: blur(20px);
|
|
44
|
-
mask-image: linear-gradient(
|
|
45
|
-
to bottom,
|
|
46
|
-
black 0 50%,
|
|
47
|
-
transparent 50% 100%
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
&::after {
|
|
52
|
-
backdrop-filter: blur(5px);
|
|
53
|
-
background: rgba(38, 51, 54, 0.1);
|
|
54
|
-
|
|
55
|
-
mask-image: linear-gradient(
|
|
56
|
-
to bottom,
|
|
57
|
-
transparent 0 50%,
|
|
58
|
-
black 50% 51%,
|
|
59
|
-
transparent 51%
|
|
60
|
-
);
|
|
61
|
-
}
|
|
25
|
+
background: var(--card);
|
|
26
|
+
border-bottom: solid 2px var(--border);
|
|
27
|
+
border-top-left-radius: inherit;
|
|
28
|
+
border-top-right-radius: inherit;
|
|
62
29
|
|
|
63
30
|
.Table_Cell {
|
|
64
31
|
&:first-child {
|
|
65
|
-
border-top-left-radius:
|
|
32
|
+
border-top-left-radius: inherit;
|
|
66
33
|
}
|
|
67
34
|
|
|
68
35
|
&:last-child {
|
|
69
|
-
border-top-right-radius:
|
|
36
|
+
border-top-right-radius: inherit;
|
|
70
37
|
}
|
|
71
38
|
|
|
72
39
|
&:not(:empty) + &:not(:empty)::before {
|
|
73
40
|
display: block;
|
|
74
41
|
cursor: col-resize;
|
|
75
|
-
content:
|
|
42
|
+
content: "";
|
|
76
43
|
inset: var(--gap) auto var(--gap) 0;
|
|
77
44
|
position: absolute;
|
|
78
45
|
border-left: 1px solid var(--border);
|
|
@@ -85,10 +52,15 @@
|
|
|
85
52
|
}
|
|
86
53
|
}
|
|
87
54
|
|
|
55
|
+
.Table_Head-sticky {
|
|
56
|
+
position: sticky;
|
|
57
|
+
top: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
88
60
|
.Table_Row {
|
|
89
61
|
align-items: center;
|
|
90
62
|
|
|
91
|
-
&:not(:hover) .
|
|
63
|
+
&:not(:hover) .Table_Cell-shy {
|
|
92
64
|
visibility: hidden;
|
|
93
65
|
}
|
|
94
66
|
|
|
@@ -98,33 +70,32 @@
|
|
|
98
70
|
}
|
|
99
71
|
|
|
100
72
|
.Table_Cell {
|
|
73
|
+
position: relative;
|
|
74
|
+
padding: 0.75em 1em;
|
|
75
|
+
align-items: center;
|
|
101
76
|
white-space: nowrap;
|
|
102
77
|
overflow: hidden;
|
|
103
78
|
text-overflow: ellipsis;
|
|
104
|
-
padding: 1em;
|
|
105
|
-
position: relative;
|
|
106
|
-
display: flex;
|
|
107
|
-
align-items: center;
|
|
108
79
|
gap: 2px;
|
|
109
80
|
|
|
81
|
+
> code {
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
}
|
|
84
|
+
|
|
110
85
|
&:has(img) {
|
|
111
86
|
padding-block: 0;
|
|
112
87
|
}
|
|
113
88
|
|
|
114
|
-
> img,
|
|
89
|
+
> img,
|
|
90
|
+
> a > img {
|
|
115
91
|
height: 2em;
|
|
116
92
|
/* margin: 4px; */
|
|
117
93
|
display: inline-block;
|
|
118
94
|
border-radius: 99px;
|
|
119
95
|
max-width: none;
|
|
120
96
|
}
|
|
121
|
-
}
|
|
122
97
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.Table-controls {
|
|
129
|
-
text-align: right;
|
|
98
|
+
&-controls {
|
|
99
|
+
text-align: right;
|
|
100
|
+
}
|
|
130
101
|
}
|
|
@@ -12,7 +12,7 @@ module CafeCar
|
|
|
12
12
|
def index
|
|
13
13
|
@examples = view_context.template_glob("cafe_car/examples/ui/*")
|
|
14
14
|
.map { _1.name.sub(/\..+$/, "") }
|
|
15
|
-
.to_h { [_1.camelize, "cafe_car/examples/ui/#{_1}"] }
|
|
15
|
+
.to_h { [ _1.camelize, "cafe_car/examples/ui/#{_1}" ] }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
private
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class SessionsController < const(:ApplicationController)
|
|
3
|
+
include Controller
|
|
4
|
+
|
|
5
|
+
cafe_car model: CafeCar[:Session]
|
|
6
|
+
rate_limit to: 10, within: 3.minutes, only: :create # , with: -> { redirect_to new_session_path, alert: "Try again later." }
|
|
7
|
+
before_action :skip_policy_scope, except: :index
|
|
8
|
+
|
|
9
|
+
after_create :persist_session
|
|
10
|
+
after_destroy :terminate_session
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
run_callbacks(:create) { object.save! }
|
|
14
|
+
respond_with object, location: after_authentication_url
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def destroy
|
|
18
|
+
run_callbacks(:destroy) { object.destroy! }
|
|
19
|
+
respond_with object, location: main_app.root_path
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def find_object
|
|
25
|
+
self.object = current_session
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def build_object = find_object
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
module Authentication
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
helper_method :authenticated?, :current_user, :current_session
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Sessions/login are opt-in: a host enables them by running the
|
|
12
|
+
# `cafe_car:sessions` generator and exposing the session routes. When that
|
|
13
|
+
# infrastructure is absent we can't redirect to a login page, so callers
|
|
14
|
+
# fall back to 403 Forbidden instead of 500ing on a missing route.
|
|
15
|
+
def sessions_available?
|
|
16
|
+
respond_to?(:new_session_path) && CafeCar.sessions_available?
|
|
17
|
+
rescue StandardError
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def authenticated?
|
|
22
|
+
current_user
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def current_user
|
|
26
|
+
current_session.user
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def current_session
|
|
30
|
+
CafeCar[:Current].session ||= find_session_by_cookie || build_session
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def build_session
|
|
34
|
+
CafeCar[:Session].new user_agent: request.user_agent,
|
|
35
|
+
ip_address: request.remote_ip
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def terminate_session
|
|
39
|
+
CafeCar[:Current].session.destroy!
|
|
40
|
+
CafeCar[:Current].session = nil
|
|
41
|
+
cookies.delete(:session_id)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def persist_session
|
|
45
|
+
cookies.signed.permanent[:session_id] = { value: current_session.id, httponly: true, same_site: :lax }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def find_session_by_cookie
|
|
49
|
+
cookies.signed[:session_id].try { Session.find(_1) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def request_authentication
|
|
53
|
+
session[:return_to_after_authenticating] = request.url
|
|
54
|
+
redirect_to new_session_path, warning: t(:auth_required, new_session: t(:new_session))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def after_authentication_url
|
|
58
|
+
session.delete(:return_to_after_authenticating) || root_url
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/app/javascript/cafe_car.js
CHANGED
|
@@ -16,29 +16,34 @@ addEventListener("mousedown", event => {
|
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
addEventListener("mouseup", event => {
|
|
19
|
-
|
|
19
|
+
let isClose = event.target.closest(".Close") || event.target.matches(".Modal")
|
|
20
|
+
if (isClose && event.target === window.mouseDownTarget) {
|
|
20
21
|
event.preventDefault()
|
|
21
22
|
event.stopPropagation()
|
|
22
|
-
event.target.closest(".
|
|
23
|
+
event.target.closest(".popup").classList.add("remove")
|
|
23
24
|
}
|
|
24
25
|
}, { capture: true })
|
|
25
26
|
|
|
26
27
|
addEventListener("keydown", event => {
|
|
27
28
|
switch (event.key) {
|
|
28
29
|
case "Escape":
|
|
29
|
-
let
|
|
30
|
-
last(document.querySelectorAll(".
|
|
31
|
-
if (
|
|
30
|
+
let popup = event.target.closest(".popup") ||
|
|
31
|
+
last(document.querySelectorAll(".popup"))
|
|
32
|
+
if (popup) popup.classList.add("remove");
|
|
32
33
|
}
|
|
33
34
|
})
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
36
|
+
function animationEnd({ target }) {
|
|
37
|
+
if (target.matches(".remove")) target.remove()
|
|
38
|
+
else if (target.matches(".popup")) {
|
|
39
|
+
let input = target.querySelector("input:not([type=hidden]), textarea")
|
|
40
|
+
input?.focus()
|
|
41
|
+
input?.setSelectionRange(-1, -1)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
38
44
|
|
|
39
|
-
addEventListener("
|
|
40
|
-
|
|
41
|
-
})
|
|
45
|
+
addEventListener("animationend", animationEnd)
|
|
46
|
+
addEventListener("transitionend", animationEnd)
|
|
42
47
|
|
|
43
48
|
// NOTE: field-sizing property is used instead
|
|
44
49
|
// function adjustHeight(textarea) {
|