avo 4.0.0.beta.30 → 4.0.0.beta.32
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/Gemfile.lock +1 -1
- data/app/assets/builds/avo/application.css +222 -158
- data/app/assets/builds/avo/application.js +2 -2
- data/app/assets/builds/avo/application.js.map +3 -3
- data/app/assets/images/avo/favicon-dark.ico +0 -0
- data/app/assets/images/avo/logo-dark.png +0 -0
- data/app/assets/images/avo/logomark-dark.png +0 -0
- data/app/assets/stylesheets/css/components/breadcrumbs.css +44 -4
- data/app/assets/stylesheets/css/components/color_scheme_switcher.css +17 -22
- data/app/assets/stylesheets/css/components/ui/card.css +5 -0
- data/app/assets/stylesheets/css/components/ui/description_list.css +1 -1
- data/app/assets/stylesheets/css/fields/code.css +1 -1
- data/app/assets/stylesheets/css/layout.css +187 -38
- data/app/assets/stylesheets/css/scrollbar.css +12 -0
- data/app/assets/stylesheets/css/sidebar.css +2 -2
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +3 -3
- data/app/javascript/js/controllers/actions_overflow_controller.js +22 -3
- data/app/javascript/js/controllers/sidebar_controller.js +11 -0
- data/app/views/avo/partials/_footer.html.erb +1 -1
- data/app/views/avo/partials/_navbar.html.erb +31 -49
- data/app/views/layouts/avo/application.html.erb +20 -24
- data/lib/avo/concerns/breadcrumbs.rb +1 -0
- data/lib/avo/configuration/appearance.rb +27 -37
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/locales/avo.ar.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.de.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.en.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.es.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.fr.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.it.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.ja.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.nb.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.nl.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.nn.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.pl.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.pt-BR.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.pt.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.ro.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.ru.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.tr.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.ua.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.zh-TW.yml +1 -0
- data/lib/generators/avo/templates/locales/avo.zh.yml +1 -0
- metadata +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
.breadcrumbs {
|
|
2
|
-
@apply w-full;
|
|
2
|
+
@apply sticky z-40 w-full bg-primary mb-4;
|
|
3
|
+
/* top: calc(var(--top-navbar-height) + var(--spacing) * 4); */
|
|
4
|
+
top: calc(var(--top-navbar-height) + var(--spacing) * 2);
|
|
5
|
+
margin-top: calc(var(--spacing) * -2 + 1px);
|
|
6
|
+
|
|
7
|
+
/* Fake background so the content scrolling underneath the breadcrumbs doesn't look like it's bleeding through */
|
|
8
|
+
&::before {
|
|
9
|
+
content: "";
|
|
10
|
+
position: absolute;
|
|
11
|
+
width: 100%;
|
|
12
|
+
top: 0;
|
|
13
|
+
left: 0;
|
|
14
|
+
right: 0;
|
|
15
|
+
height: calc(0.5rem);
|
|
16
|
+
transform: translateY(-100%);
|
|
17
|
+
background: var(--color-primary);
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Soft fade strip just below the sticky breadcrumbs — eases the seam between
|
|
23
|
+
the breadcrumb bar and the content scrolling underneath. */
|
|
24
|
+
.breadcrumbs::after {
|
|
25
|
+
content: "";
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: auto;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
right: 0;
|
|
31
|
+
transform: translateY(100%);
|
|
32
|
+
height: --spacing(4);
|
|
33
|
+
background: linear-gradient(
|
|
34
|
+
to bottom,
|
|
35
|
+
var(--color-primary),
|
|
36
|
+
color-mix(in oklab, var(--color-primary), transparent 100%)
|
|
37
|
+
);
|
|
38
|
+
pointer-events: none;
|
|
3
39
|
}
|
|
4
40
|
|
|
5
41
|
.breadcrumbs__container {
|
|
@@ -11,10 +47,10 @@
|
|
|
11
47
|
}
|
|
12
48
|
|
|
13
49
|
.breadcrumb-element {
|
|
14
|
-
@apply inline-flex items-center gap-2 font-medium leading-6 text-sm rounded-lg
|
|
50
|
+
@apply inline-flex items-center gap-2 font-medium leading-6 text-sm rounded-lg px-1 py-0.5 text-content-secondary;
|
|
15
51
|
|
|
16
52
|
&:first-child {
|
|
17
|
-
@apply
|
|
53
|
+
@apply -ms-1;
|
|
18
54
|
}
|
|
19
55
|
}
|
|
20
56
|
|
|
@@ -42,6 +78,10 @@
|
|
|
42
78
|
}
|
|
43
79
|
|
|
44
80
|
&:hover {
|
|
45
|
-
@apply bg-
|
|
81
|
+
@apply bg-tertiary;
|
|
82
|
+
|
|
83
|
+
.breadcrumb-element__text {
|
|
84
|
+
@apply text-content;
|
|
85
|
+
}
|
|
46
86
|
}
|
|
47
87
|
}
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
@apply inline-flex items-center;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
/* The switcher's trigger surfaces (inline pill + compact trigger) sit on the
|
|
7
|
+
dark top-navbar in both schemes, so they use primitive neutral tokens that
|
|
8
|
+
don't flip with light/dark — matching breadcrumbs and the sidebar toggle.
|
|
9
|
+
The popover panels below stay theme-adaptive because they render on the
|
|
10
|
+
page surface, not the navbar. */
|
|
11
|
+
|
|
6
12
|
/* Inline pill — three sections sit in a single rounded container */
|
|
7
13
|
.color-scheme-switcher--inline {
|
|
8
|
-
@apply gap-1 p-1 rounded-lg bg-
|
|
14
|
+
@apply gap-1 p-1 rounded-lg bg-avo-neutral-800 border border-avo-neutral-700;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
/* Compact — a single icon trigger that opens a unified panel */
|
|
@@ -15,7 +21,7 @@
|
|
|
15
21
|
|
|
16
22
|
.color-scheme-switcher__compact-trigger {
|
|
17
23
|
@apply inline-flex items-center gap-1 h-8 ps-2 pe-1.5 rounded-lg transition-all duration-150;
|
|
18
|
-
@apply text-
|
|
24
|
+
@apply text-avo-neutral-300 bg-avo-neutral-800 border border-avo-neutral-700 hover:bg-avo-neutral-700 hover:text-avo-neutral-50;
|
|
19
25
|
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-400 focus-visible:ring-offset-1;
|
|
20
26
|
@apply cursor-pointer;
|
|
21
27
|
}
|
|
@@ -114,33 +120,22 @@
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
.color-scheme-switcher__button {
|
|
117
|
-
@apply inline-flex items-center justify-center p-1.5 rounded-md transition-all duration-150 text-
|
|
123
|
+
@apply inline-flex items-center justify-center p-1.5 rounded-md transition-all duration-150 text-avo-neutral-300 hover:bg-avo-neutral-700 hover:text-avo-neutral-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-400 focus-visible:ring-offset-1 active:scale-95;
|
|
118
124
|
@apply border-0 bg-transparent cursor-pointer;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
/* Scheme buttons - active
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
.scheme-light .color-scheme-switcher__button[data-scheme="light"] {
|
|
127
|
-
@apply bg-primary text-content shadow-sm;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.color-scheme-switcher__button[data-scheme="dark"] {
|
|
131
|
-
@apply bg-transparent text-content-secondary shadow-none;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.scheme-dark .color-scheme-switcher__button[data-scheme="dark"] {
|
|
135
|
-
@apply bg-primary text-content shadow-sm;
|
|
136
|
-
}
|
|
137
|
-
|
|
127
|
+
/* Scheme buttons - idle vs active state. The active "pill" stands out against
|
|
128
|
+
the neutral-800 inline pill background by going darker (neutral-950). */
|
|
129
|
+
.color-scheme-switcher__button[data-scheme="light"],
|
|
130
|
+
.color-scheme-switcher__button[data-scheme="dark"],
|
|
138
131
|
.color-scheme-switcher__button[data-scheme="auto"] {
|
|
139
|
-
@apply bg-transparent text-
|
|
132
|
+
@apply bg-transparent text-avo-neutral-300 shadow-none;
|
|
140
133
|
}
|
|
141
134
|
|
|
135
|
+
.scheme-light .color-scheme-switcher__button[data-scheme="light"],
|
|
136
|
+
.scheme-dark .color-scheme-switcher__button[data-scheme="dark"],
|
|
142
137
|
.scheme-auto .color-scheme-switcher__button[data-scheme="auto"] {
|
|
143
|
-
@apply bg-
|
|
138
|
+
@apply bg-avo-neutral-950 text-avo-neutral-50 shadow-sm;
|
|
144
139
|
}
|
|
145
140
|
|
|
146
141
|
.color-scheme-switcher__icon {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/* Card Component - Based on Figma Design System */
|
|
2
2
|
.card {
|
|
3
3
|
@apply flex flex-col items-start self-stretch rounded-xl border border-tertiary bg-secondary;
|
|
4
|
+
|
|
5
|
+
/* Contain horizontal overflow so a wide inner scroller (e.g. .card__wrapper
|
|
6
|
+
wrapping a wide table) can't bleed past the card and push the document
|
|
7
|
+
wider than the viewport on narrow screens. */
|
|
8
|
+
overflow-x: clip;
|
|
4
9
|
}
|
|
5
10
|
|
|
6
11
|
/* Card variants */
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
--top-navbar-height:
|
|
1
|
+
@theme {
|
|
2
|
+
--top-navbar-height: 3.5rem;
|
|
3
|
+
|
|
4
|
+
--navbar-bg: var(--color-avo-neutral-900);
|
|
5
|
+
--navbar-notch-radius: 1rem;
|
|
6
|
+
--navbar-notch-color: var(--navbar-bg);
|
|
7
|
+
|
|
8
|
+
--border-color: var(--color-tertiary);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@layer theme {
|
|
12
|
+
.dark {
|
|
13
|
+
--border-color: var(--color-background);
|
|
14
|
+
}
|
|
3
15
|
}
|
|
4
16
|
|
|
5
17
|
.skip-to-content {
|
|
@@ -24,47 +36,44 @@
|
|
|
24
36
|
height: var(--top-navbar-height);
|
|
25
37
|
}
|
|
26
38
|
|
|
27
|
-
/*
|
|
28
|
-
|
|
29
|
-
.main
|
|
39
|
+
/* Layout variables live on the outermost sidebar host so they cascade to both
|
|
40
|
+
the top-navbar and .main (which are siblings, not parent/child).
|
|
41
|
+
--content-width is intentionally relative to .main's content box (which is
|
|
42
|
+
already offset by --sidebar-offset-size via padding), so centering via
|
|
43
|
+
mx-auto happens inside the area to the right of the sidebar — not the page. */
|
|
44
|
+
[data-controller~="sidebar"] {
|
|
30
45
|
--sidebar-width: --spacing(64);
|
|
31
46
|
--sidebar-offset-size: 0rem;
|
|
32
|
-
--content-width: calc(100% - var(--
|
|
47
|
+
--content-width: calc(100% - var(--spacing));
|
|
48
|
+
}
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
50
|
+
@media (min-width: theme(--breakpoint-lg)) {
|
|
51
|
+
[data-controller~="sidebar"][data-sidebar-open-value="true"] {
|
|
52
|
+
--sidebar-offset-size: var(--sidebar-width);
|
|
38
53
|
}
|
|
54
|
+
}
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
[data-controller~="sidebar"][data-sidebar-open-value="false"] {
|
|
57
|
+
.main-content {
|
|
58
|
+
@apply ms-0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
.main {
|
|
63
|
+
padding-top: var(--top-navbar-height);
|
|
64
|
+
padding-inline-start: calc(var(--sidebar-offset-size) - var(--spacing));
|
|
65
|
+
transition: padding 0.1s ease-in-out;
|
|
66
|
+
}
|
|
44
67
|
|
|
45
|
-
|
|
68
|
+
.main-content {
|
|
69
|
+
@apply w-(--content-width) flex flex-col bg-primary py-2 lg:py-4 px-2 lg:px-4 border-s border-(--border-color) ms-1;
|
|
70
|
+
transition: margin 0.1s ease-in-out, width 0.1s ease-in-out;
|
|
71
|
+
min-height: calc(100dvh - var(--top-navbar-height) - var(--spacing));
|
|
72
|
+
}
|
|
46
73
|
|
|
47
|
-
height: calc(100dvh - var(--spacing) - var(--top-navbar-height));
|
|
48
74
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@apply px-0.5; /* add some space for the view type toggle buttons to not be cut off by the overflow. this might be needed to be handled differently with a wrapper inside the scrolling area */
|
|
52
|
-
@apply pt-1; /* add some space for the button focus ring to not be cut off by the overflow */
|
|
53
|
-
}
|
|
54
|
-
.avo-container {
|
|
55
|
-
@apply flex flex-1 flex-col justify-between;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
&.sidebar-open {
|
|
60
|
-
/* Add padding to the main area to allow for the sidebar to expand. */
|
|
61
|
-
.main-content-area {
|
|
62
|
-
@media (min-width: theme(--breakpoint-lg)) {
|
|
63
|
-
margin-inline-start: calc(var(--sidebar-width) + var(--spacing));
|
|
64
|
-
width: var(--content-width);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
75
|
+
.main-content__container {
|
|
76
|
+
@apply flex flex-1 flex-col px-2;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
.container-large {
|
|
@@ -80,7 +89,130 @@
|
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
.top-navbar {
|
|
83
|
-
@apply z-100 fixed
|
|
92
|
+
@apply z-100 fixed top-0 inset-x-0 w-full shrink-0 pointer-events-none;
|
|
93
|
+
|
|
94
|
+
/* Three-region grid: start | center | end. start/end share remaining space
|
|
95
|
+
equally (1fr each) so the center column (search) stays visually anchored
|
|
96
|
+
to the navbar's midpoint regardless of breadcrumb length. */
|
|
97
|
+
display: grid;
|
|
98
|
+
grid-template-columns: 1fr auto 1fr;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: 1rem;
|
|
101
|
+
padding-inline: 0.75rem;
|
|
102
|
+
|
|
103
|
+
background-color: var(--navbar-bg);
|
|
104
|
+
|
|
105
|
+
> * {
|
|
106
|
+
@apply pointer-events-auto;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.breadcrumbs__container {
|
|
110
|
+
@apply flex-nowrap overflow-x-auto;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Inverted arches at the top corners of the content area — a square just
|
|
114
|
+
below the navbar painted with the notch color, with a quarter-circle cut
|
|
115
|
+
out of one bottom corner. The cut reveals the content beneath, so the
|
|
116
|
+
content looks like it has a rounded top corner without actually applying
|
|
117
|
+
border-radius to it (which would clip its scroll). */
|
|
118
|
+
&::before,
|
|
119
|
+
&::after {
|
|
120
|
+
content: "";
|
|
121
|
+
position: absolute;
|
|
122
|
+
top: 100%;
|
|
123
|
+
width: var(--navbar-notch-radius);
|
|
124
|
+
height: var(--navbar-notch-radius);
|
|
125
|
+
pointer-events: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&::before {
|
|
129
|
+
left: 0;
|
|
130
|
+
background: radial-gradient(
|
|
131
|
+
circle var(--navbar-notch-radius) at 100% 100%,
|
|
132
|
+
transparent 99%,
|
|
133
|
+
var(--navbar-notch-color) 100%
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&::after {
|
|
138
|
+
right: 0;
|
|
139
|
+
background: radial-gradient(
|
|
140
|
+
circle var(--navbar-notch-radius) at 0% 100%,
|
|
141
|
+
transparent 99%,
|
|
142
|
+
var(--navbar-notch-color) 100%
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.top-navbar__start {
|
|
148
|
+
@apply flex items-center gap-3 min-w-0 h-full;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.top-navbar__logo {
|
|
152
|
+
@apply shrink-0;
|
|
153
|
+
|
|
154
|
+
height: calc(var(--top-navbar-height) - --spacing(6));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.top-navbar__center {
|
|
158
|
+
@apply flex items-center justify-center min-w-0;
|
|
159
|
+
|
|
160
|
+
/* Search input is rendered against the dark navbar in both light/dark
|
|
161
|
+
themes, so it uses primitive neutral tokens that are stable across modes
|
|
162
|
+
— matching the breadcrumbs and sidebar-toggle treatment. */
|
|
163
|
+
.search-input__input {
|
|
164
|
+
background-color: var(--color-avo-neutral-800);
|
|
165
|
+
border-color: var(--color-avo-neutral-700);
|
|
166
|
+
color: var(--color-avo-neutral-50);
|
|
167
|
+
|
|
168
|
+
&::placeholder {
|
|
169
|
+
color: var(--color-avo-neutral-400);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.search-input__prefix,
|
|
174
|
+
.search-input__suffix {
|
|
175
|
+
color: var(--color-avo-neutral-400);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.search-input__suffix kbd {
|
|
179
|
+
background-color: var(--color-avo-neutral-700);
|
|
180
|
+
border-color: var(--color-avo-neutral-600);
|
|
181
|
+
color: var(--color-avo-neutral-200);
|
|
182
|
+
box-shadow: none;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* On mobile, collapse the navbar search to just the input + search icon —
|
|
186
|
+
hide the keyboard shortcut suffix and drop the reserved end-padding so it
|
|
187
|
+
stays compact next to the logo. */
|
|
188
|
+
@media (width < theme(--breakpoint-sm)) {
|
|
189
|
+
.search-input__suffix {
|
|
190
|
+
display: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.search-input__input.search-input__input--with-shortcut,
|
|
194
|
+
.search-input__input.search-input__input--with-two-key-shortcut {
|
|
195
|
+
padding-inline-end: calc(
|
|
196
|
+
var(--input-icon-offset) +
|
|
197
|
+
var(--input-icon-gap)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.top-navbar__end {
|
|
204
|
+
@apply flex items-center justify-end gap-4 min-w-0 h-full;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Links inside the always-dark navbar use primitive neutral tokens so they
|
|
208
|
+
stay readable in both light and dark mode — matching the search input and
|
|
209
|
+
sidebar toggle treatment. */
|
|
210
|
+
.top-navbar a {
|
|
211
|
+
color: var(--color-avo-neutral-300);
|
|
212
|
+
|
|
213
|
+
&:hover {
|
|
214
|
+
color: var(--color-avo-neutral-50);
|
|
215
|
+
}
|
|
84
216
|
}
|
|
85
217
|
|
|
86
218
|
/* Sidebar toggle: choose icon via navbar variables; visibility driven by data attribute */
|
|
@@ -100,14 +232,31 @@ button[data-sidebar-state="closed"] .sidebar-toggle-icon--closed {
|
|
|
100
232
|
@apply inline-flex;
|
|
101
233
|
}
|
|
102
234
|
|
|
103
|
-
/*
|
|
235
|
+
/* The toggle sits on the dark top-navbar, so it uses primitive neutral tokens
|
|
236
|
+
that are stable across light/dark — matching the breadcrumbs treatment. */
|
|
237
|
+
button[data-sidebar-toggle-icon] {
|
|
238
|
+
--btn-text-color: var(--color-avo-neutral-300);
|
|
239
|
+
|
|
240
|
+
&:hover {
|
|
241
|
+
--btn-text-color: var(--color-avo-neutral-50);
|
|
242
|
+
background-color: var(--color-avo-neutral-800);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Default hidden state - LTR: sidebar slides in from left.
|
|
247
|
+
Sidebar starts below the navbar (matching .main's padding-top), so the
|
|
248
|
+
navbar is uninterrupted across the full viewport width. */
|
|
104
249
|
.avo-sidebar {
|
|
105
|
-
@apply fixed z-
|
|
250
|
+
@apply fixed z-50 start-0 flex-1 border-e border-tertiary lg:border-none bg-background lg:bg-transparent w-(--sidebar-width) dark:bg-primary;
|
|
106
251
|
|
|
252
|
+
|
|
253
|
+
top: var(--top-navbar-height);
|
|
254
|
+
height: calc(100dvh - var(--top-navbar-height));
|
|
107
255
|
transform: translateX(-100%);
|
|
108
|
-
transition: transform 0.1s ease;
|
|
256
|
+
transition: transform 0.1s ease-in-out;
|
|
109
257
|
}
|
|
110
258
|
|
|
259
|
+
|
|
111
260
|
/* RTL: sidebar slides in from right when hidden */
|
|
112
261
|
html[dir="rtl"] .avo-sidebar {
|
|
113
262
|
transform: translateX(100%);
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* Keep auto-scrolled targets (focus, scrollIntoView, anchor jumps, hash links)
|
|
2
|
+
below the sticky navbar. Applied to every likely scroll container so it
|
|
3
|
+
works whether the document, the body, or the main content area is what
|
|
4
|
+
actually scrolls. */
|
|
5
|
+
html,
|
|
6
|
+
body,
|
|
7
|
+
.main-content,
|
|
8
|
+
.scrollable-wrapper {
|
|
9
|
+
scroll-padding-top: calc(var(--top-navbar-height) + var(--spacing) * 2);
|
|
10
|
+
scroll-padding-bottom: calc(var(--spacing) * 2);
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
/* total width */
|
|
2
14
|
body.os-pc .mac-styled-scrollbar::-webkit-scrollbar {
|
|
3
15
|
background-color: none;
|
|
@@ -285,11 +285,11 @@
|
|
|
285
285
|
/* ================================================ */
|
|
286
286
|
|
|
287
287
|
.sidebar-status {
|
|
288
|
-
@apply p-4 border-t border-
|
|
288
|
+
@apply p-4 border-t border-(--border-color);
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
.sidebar-status__link {
|
|
292
|
-
@apply px-4 py-2 border border-
|
|
292
|
+
@apply px-4 py-2 border border-(--border-color) rounded-sm flex justify-between items-center w-full text-content text-sm;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
.sidebar-status__indicator {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
data-association="<%= @field.id %>"
|
|
6
6
|
data-association-class="<%= @field&.target_resource&.model_class || nil %>"
|
|
7
7
|
>
|
|
8
|
-
<div class="
|
|
8
|
+
<div class="w-full flex flex-wrap">
|
|
9
9
|
<%= field_wrapper(**field_wrapper_args, label_for: @field.polymorphic_form_field_label, help: @field.polymorphic_help || '') do %>
|
|
10
10
|
<%= @form.select @field.type_input_foreign_key, @field.types.map { |type| [Avo.resource_manager.get_resource_by_model_class(type.to_s).name, type.to_s] },
|
|
11
11
|
{
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
<%= @form.hidden_field @field.type_input_foreign_key %>
|
|
30
30
|
<% end %>
|
|
31
31
|
<% end %>
|
|
32
|
-
<div data-belongs-to-field-target="container" class="hidden
|
|
32
|
+
<div data-belongs-to-field-target="container" class="hidden <%= @field.width_class %>"></div>
|
|
33
33
|
<% @field.types.each do |type| %>
|
|
34
34
|
<template data-belongs-to-field-target="type" data-type="<%= type %>">
|
|
35
35
|
<div data-polymorphic-type="<%= type %>">
|
|
36
|
-
<%= field_wrapper(**field_wrapper_args.merge!(data: reload_data), label: Avo.resource_manager.get_resource_by_model_class(type.to_s).name) do %>
|
|
36
|
+
<%= field_wrapper(**field_wrapper_args.merge!(data: reload_data), label: Avo.resource_manager.get_resource_by_model_class(type.to_s).name, class: "!w-full") do %>
|
|
37
37
|
<% if @field.is_searchable? %>
|
|
38
38
|
<%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: @form,
|
|
39
39
|
disabled: disabled,
|
|
@@ -7,11 +7,30 @@ export default class extends Controller {
|
|
|
7
7
|
panelList: Boolean,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Table/grid on index pages; main content region on show/edit and other non-list views.
|
|
11
11
|
get parentTarget() {
|
|
12
12
|
return document.querySelector('[data-component-name="avo/view_types/table_component"]')
|
|
13
13
|
|| document.querySelector('[data-component-name="avo/view_types/grid_component"]')
|
|
14
|
-
|| document.querySelector('
|
|
14
|
+
|| document.querySelector('#main-content')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Parent nodes grow with page content, but overflow should be measured against
|
|
18
|
+
// the visible viewport — the old .scrollable-wrapper had a fixed height for this.
|
|
19
|
+
get visibleParentDimensions() {
|
|
20
|
+
const parent = this.parentTarget
|
|
21
|
+
|
|
22
|
+
if (!parent) {
|
|
23
|
+
return { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const rect = parent.getBoundingClientRect()
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
top: rect.top,
|
|
30
|
+
left: rect.left,
|
|
31
|
+
right: rect.right,
|
|
32
|
+
bottom: Math.min(rect.bottom, window.innerHeight),
|
|
33
|
+
}
|
|
15
34
|
}
|
|
16
35
|
|
|
17
36
|
// Check if the document is in RTL mode
|
|
@@ -28,7 +47,7 @@ export default class extends Controller {
|
|
|
28
47
|
this.element.style.display = 'block'
|
|
29
48
|
this.childDimensions = this.contentTarget.getBoundingClientRect()
|
|
30
49
|
this.element.style.display = ''
|
|
31
|
-
this.parentDimensions = this.
|
|
50
|
+
this.parentDimensions = this.visibleParentDimensions
|
|
32
51
|
|
|
33
52
|
this.adjustOverflow()
|
|
34
53
|
}
|
|
@@ -121,12 +121,23 @@ export default class extends Controller {
|
|
|
121
121
|
toggleSidebar() {
|
|
122
122
|
if (this.sidebarTarget.classList.contains('hidden')) {
|
|
123
123
|
this.sidebarTarget.classList.remove('hidden')
|
|
124
|
+
|
|
125
|
+
// The sidebar starts the session as display:none when closed. Removing
|
|
126
|
+
// `hidden` and toggling `sidebar-open` in the same tick would let the
|
|
127
|
+
// browser collapse both style changes into one repaint — meaning the
|
|
128
|
+
// transform: translateX transition would never fire on the very first
|
|
129
|
+
// open. Force a reflow so the browser commits the post-hidden state
|
|
130
|
+
// (translateX(-100%) visible) before we flip to translateX(0).
|
|
131
|
+
this.mainAreaTarget.offsetHeight // eslint-disable-line no-unused-expressions
|
|
124
132
|
}
|
|
125
133
|
this.mainAreaTarget.classList.toggle('sidebar-open')
|
|
126
134
|
|
|
127
135
|
Cookies.set(this.cookieKey, this.newValue(Cookies.get(this.cookieKey)))
|
|
128
136
|
|
|
129
137
|
const isOpen = this.mainAreaTarget.classList.contains('sidebar-open')
|
|
138
|
+
// Keep the controller's openValue in sync so --sidebar-offset-size flips,
|
|
139
|
+
// which drives the navbar spacer width and .main padding transitions.
|
|
140
|
+
this.openValue = isOpen
|
|
130
141
|
this.setToggleButtonsState(isOpen ? 'open' : 'closed')
|
|
131
142
|
}
|
|
132
143
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<div class="text-center text-sm text-
|
|
1
|
+
<div class="text-center text-sm text-content-secondary <%= 'print:hidden' if Avo.configuration.hide_layout_when_printing %>">
|
|
2
2
|
<a href="https://avohq.io/" target="_blank">Avo</a> · © <%= Date.today.year %> AvoHQ · v<%= Avo::VERSION %>
|
|
3
3
|
</div>
|
|
@@ -1,60 +1,42 @@
|
|
|
1
1
|
<%
|
|
2
|
-
# Icons shown when sidebar is open (collapse) vs closed (expand). Change these to customize.
|
|
3
2
|
sidebar_open_icon = 'layout-sidebar-left-collapse'
|
|
4
3
|
sidebar_closed_icon = 'layout-sidebar-left-expand'
|
|
5
4
|
sidebar_state = @sidebar_open ? 'open' : 'closed'
|
|
6
5
|
%>
|
|
7
6
|
<%= content_tag :nav, class: class_names("top-navbar", {"print:hidden": Avo.configuration.hide_layout_when_printing}) do %>
|
|
8
|
-
<div class="
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
<div class="top-navbar__start">
|
|
8
|
+
<%# Mobile always needs a toggle to reach the overlay sidebar. Desktop toggle
|
|
9
|
+
is optional and controlled by sidebar_toggle_visible. %>
|
|
10
|
+
<%= a_button class: class_names('shrink-0', 'lg:hidden': !Avo.configuration.sidebar_toggle_visible),
|
|
11
|
+
size: :sm,
|
|
12
|
+
style: :text,
|
|
13
|
+
data: {
|
|
14
|
+
action: 'click->sidebar#toggleSidebarForViewport',
|
|
15
|
+
sidebar_toggle_icon: true,
|
|
16
|
+
sidebar_state: sidebar_state
|
|
17
|
+
},
|
|
18
|
+
aria: { label: "Toggle sidebar" } do %>
|
|
19
|
+
<span class="sidebar-toggle-icon sidebar-toggle-icon--open">
|
|
20
|
+
<%= svg sidebar_open_icon, class: 'h-4 button__icon' %>
|
|
21
|
+
</span>
|
|
22
|
+
<span class="sidebar-toggle-icon sidebar-toggle-icon--closed">
|
|
23
|
+
<%= svg sidebar_closed_icon, class: 'h-4 button__icon' %>
|
|
24
|
+
</span>
|
|
25
|
+
<% end %>
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
size: :sm,
|
|
30
|
-
style: :text,
|
|
31
|
-
data: {
|
|
32
|
-
action: 'click->sidebar#toggleSidebar',
|
|
33
|
-
sidebar_toggle_icon: true,
|
|
34
|
-
sidebar_state: sidebar_state
|
|
35
|
-
},
|
|
36
|
-
aria: { label: "Toggle sidebar" } do %>
|
|
37
|
-
<span class="sidebar-toggle-icon sidebar-toggle-icon--open">
|
|
38
|
-
<%= svg sidebar_open_icon, class: 'h-4 button__icon' %>
|
|
39
|
-
</span>
|
|
40
|
-
<span class="sidebar-toggle-icon sidebar-toggle-icon--closed">
|
|
41
|
-
<%= svg sidebar_closed_icon, class: 'h-4 button__icon' %>
|
|
42
|
-
</span>
|
|
43
|
-
<% end %>
|
|
44
|
-
<% end %>
|
|
27
|
+
<div class="top-navbar__logo">
|
|
28
|
+
<%= render partial: "avo/partials/logo" %>
|
|
45
29
|
</div>
|
|
46
|
-
<%= render partial: "avo/partials/logo" %>
|
|
47
30
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<%= render
|
|
52
|
-
|
|
53
|
-
|
|
31
|
+
|
|
32
|
+
<div class="top-navbar__center">
|
|
33
|
+
<% if defined?(Avo::Pro::GlobalSearch::InputComponent) %>
|
|
34
|
+
<%= render Avo::Pro::GlobalSearch::InputComponent.new(resource: @resource) %>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="top-navbar__end">
|
|
39
|
+
<%= render partial: "avo/partials/header" %>
|
|
40
|
+
<%= render partial: "avo/partials/color_scheme_switcher" %>
|
|
54
41
|
</div>
|
|
55
42
|
<% end %>
|
|
56
|
-
<!-- This is a hack to fix the issue with the navbar not being accesible because of the .main-content-wrapper padding-->
|
|
57
|
-
<!-- TODO: when implementing the final layout (search covered issue) see if we still need this hack-->
|
|
58
|
-
<%#
|
|
59
|
-
<div class="relative opacity-0 h-16 pointer-events-none"></div>
|
|
60
|
-
%>
|