mensa 0.9.1 → 0.9.3
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/.shipkit.yml +1 -1
- data/CHANGELOG.md +51 -0
- data/Gemfile.lock +1 -1
- data/app/assets/stylesheets/mensa/application.css +1 -0
- data/app/assets/stylesheets/mensa/driver.css +4 -1
- data/app/components/mensa/control_bar/component.css +1 -5
- data/app/components/mensa/filter_pill/component.rb +15 -1
- data/app/components/mensa/filter_pill_list/component.html.erb +0 -1
- data/app/components/mensa/filter_pill_list/component_controller.js +16 -1
- data/app/components/mensa/search/component.html.erb +4 -4
- data/app/components/mensa/table/component_controller.js +46 -19
- data/app/components/mensa/table/driver.js +5 -0
- data/app/components/mensa/table/tour.css +39 -0
- data/app/components/mensa/table/tour_controller.js +1 -1
- data/app/components/mensa/views/component.css +1 -1
- data/app/components/mensa/views/component.html.erb +4 -4
- data/app/components/mensa/views/component_controller.js +22 -1
- data/app/tables/mensa/group.rb +3 -0
- data/app/views/mensa/exports/_dialog.html.erb +15 -10
- data/app/views/mensa/exports/_list.html.erb +6 -6
- data/config/importmap.rb +0 -1
- data/config/locales/en.yml +3 -0
- data/config/locales/nl.yml +3 -0
- data/lib/mensa/version.rb +1 -1
- metadata +5 -6
- data/vendor/javascript/driver.js.js +0 -666
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4dc7c940f4eff95458542c2ac2d54a71df5735105019fb09d2fc4b70f588124
|
|
4
|
+
data.tar.gz: 6f43f89a33e27295b268b6c45c4a018599e313418b1aeb4b3ae758de63180211
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a038582389be24ec077d6234974d30700bcef87ca6d3524abdecfed97bcb09280fcbbb46095af18fac2339243ebcf3f39e3cc54adad0e96519183e6e5435a67
|
|
7
|
+
data.tar.gz: 2803b2f1fa2e42b19ca2ed2aff842e7bdd2bc1315f2fef13b590010105725746f832113fec55861e8ea911eeaf18e2454b9798822d0330ad73068b472f6b7dce
|
data/.shipkit.yml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
---
|
|
2
|
-
previous_version: 0.9.
|
|
2
|
+
previous_version: 0.9.2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.3
|
|
4
|
+
|
|
5
|
+
- A Mensa table can be rendered inside a host `<form>`, such as a tab on an
|
|
6
|
+
edit page. Before, the save-view, rename-view and export dialogs (and the
|
|
7
|
+
delete button of each export) rendered their own `<form>`s. HTML has no
|
|
8
|
+
nested forms, so the outer form was closed early, its submit was silently
|
|
9
|
+
blocked by Mensa's empty `required` inputs, and the export options were
|
|
10
|
+
sent along with it. Mensa now renders no `<form>` elements, no `required`
|
|
11
|
+
attributes and no controls that a host form submits.
|
|
12
|
+
- The save-view and rename-view dialogs confirm with their button or Enter in
|
|
13
|
+
the name field. An empty name still shows the browser's validation message,
|
|
14
|
+
now from the translatable `mensa.search.component.view_name_required` and
|
|
15
|
+
`mensa.views.component.view_name_required` keys.
|
|
16
|
+
- **Behaviour change:** the export dialog's controls have a `form` attribute
|
|
17
|
+
that matches no element and names that include the table id (e.g.
|
|
18
|
+
`mensa-export-users-scope`), so they never belong to a host form and don't
|
|
19
|
+
group with radios elsewhere on the page. Select them with
|
|
20
|
+
`data-mensa-export-field="scope"` (or `export_format`, `repeat_mode`,
|
|
21
|
+
`repeat`) instead of `name`.
|
|
22
|
+
- **Behaviour change:** the search input no longer has `name="search_query"`,
|
|
23
|
+
and the `.mensa-table__export-dialog__delete-form` class is gone: an export
|
|
24
|
+
is deleted with a plain `button.mensa-table__export-dialog__delete`.
|
|
25
|
+
- Group header aggregates of `bigint` columns no longer show a trailing `.0`
|
|
26
|
+
(`3,000` instead of `3,000.0`). Postgres returns their SUM as `numeric`;
|
|
27
|
+
whole numbers are now shown without decimals.
|
|
28
|
+
- A selected view survives a page reload when the default view has filters.
|
|
29
|
+
Before, the table showed the selected view's rows, but the views dropdown
|
|
30
|
+
and the filter pills still showed the default view.
|
|
31
|
+
- A view filter that the user changes (another value or operator) becomes a
|
|
32
|
+
user filter: it's always shown, and the eye button disappears when no
|
|
33
|
+
unchanged view filters remain. Before, a pill was a view filter as long as
|
|
34
|
+
the view filtered on its column, so a changed filter could still be hidden.
|
|
35
|
+
- The views dropdown is positioned `fixed`, like the other popovers, so a
|
|
36
|
+
table with few rows (or a host element with `overflow: hidden`) no longer
|
|
37
|
+
clips it.
|
|
38
|
+
|
|
39
|
+
## 0.9.2
|
|
40
|
+
|
|
41
|
+
- The table tour no longer needs the `driver.js` npm package. Mensa now ships
|
|
42
|
+
driver.js (1.10.1, MIT) itself in `app/components/mensa/table/driver.js` and
|
|
43
|
+
imports it as `mensa/components/table/driver`, so host apps that bundle
|
|
44
|
+
Mensa's JS with esbuild (jsbundling-rails) build again without adding
|
|
45
|
+
anything. In 0.9.0 and 0.9.1 those builds failed with
|
|
46
|
+
`Could not resolve "driver.js"`.
|
|
47
|
+
- driver.js is updated from 1.3.6 to 1.10.1; its CSS
|
|
48
|
+
(`app/assets/stylesheets/mensa/driver.css`) is updated to the same version.
|
|
49
|
+
- The table tour has a dark mode. It follows the host app's Tailwind dark mode
|
|
50
|
+
setting, like the rest of Mensa.
|
|
51
|
+
- The engine's importmap no longer pins `driver.js`. Host apps that import
|
|
52
|
+
`driver.js` themselves need their own pin or npm package.
|
|
53
|
+
|
|
3
54
|
## 0.8.0
|
|
4
55
|
|
|
5
56
|
- **Behaviour change:** a column shows what was selected in SQL under its name
|
data/Gemfile.lock
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/*! driver.js 1.10.1 | https://github.com/kamranahmedse/driver.js | MIT License | Copyright (c) Kamran Ahmed */
|
|
2
|
+
/* Vendored from the driver.js npm package (dist/driver.css), unmodified. Keep in sync with
|
|
3
|
+
app/components/mensa/table/driver.js, which comes from the same version. */
|
|
4
|
+
.driver-popover{all:unset;font-family:var(--driver-popover-font-family,"Helvetica Neue", Inter, ui-sans-serif, "Apple Color Emoji", Helvetica, Arial, sans-serif);box-sizing:border-box;color:#2d2d2d;z-index:1000000000;background-color:#fff;border-radius:5px;min-width:250px;max-width:300px;margin:0;padding:15px;position:fixed;top:0;right:0;box-shadow:0 1px 10px #0006}.driver-popover-title{zoom:1;margin:0;font-size:19px;font-weight:700;line-height:1.5;display:block;position:relative}.driver-popover-close-btn{all:unset;cursor:pointer;color:#d2d2d2;z-index:1;text-align:center;width:32px;height:28px;font-size:18px;font-weight:500;transition:color .2s;position:absolute;top:0;right:0}.driver-popover-close-btn:hover,.driver-popover-close-btn:focus-visible{color:#2d2d2d}.driver-popover-title[style*=block]+.driver-popover-description{margin-top:5px}.driver-popover-description{zoom:1;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.5}.driver-popover-footer{text-align:right;zoom:1;justify-content:space-between;align-items:center;margin-top:15px;display:flex}.driver-popover-progress-text{color:#727272;zoom:1;font-size:13px;font-weight:400}.driver-popover-footer-btn{all:unset;box-sizing:border-box;color:#2d2d2d;cursor:pointer;zoom:1;background-color:#fff;border:1px solid #ccc;border-radius:3px;outline:0;padding:3px 7px;font-size:12px;line-height:1.3;text-decoration:none;display:inline-block}.driver-popover-footer .driver-popover-btn-disabled{opacity:.5;pointer-events:none}.driver-popover-footer-btn:hover,.driver-popover-footer-btn:focus{background-color:#f7f7f7}.driver-popover-navigation-btns{flex-grow:1;justify-content:flex-end;display:flex}.driver-popover-navigation-btns button+button{margin-left:4px}.driver-popover-arrow{content:"";border:5px solid #fff;position:absolute}.driver-popover-arrow-side-over{display:none}.driver-popover-arrow-side-left{border-top-color:#0000;border-bottom-color:#0000;border-right-color:#0000;left:100%}.driver-popover-arrow-side-right{border-top-color:#0000;border-bottom-color:#0000;border-left-color:#0000;right:100%}.driver-popover-arrow-side-top{border-bottom-color:#0000;border-left-color:#0000;border-right-color:#0000;top:100%}.driver-popover-arrow-side-bottom{border-top-color:#0000;border-left-color:#0000;border-right-color:#0000;bottom:100%}.driver-popover-arrow-side-center,.driver-popover-arrow-none{display:none}.driver-active .driver-overlay{pointer-events:none}.driver-active.driver-no-scroll{overflow:hidden}.driver-active *{pointer-events:none}.driver-active .driver-active-element,.driver-active .driver-active-element *,.driver-popover,.driver-popover *{pointer-events:auto}@keyframes animate-fade-in{0%{opacity:0}to{opacity:1}}.driver-fade .driver-overlay{animation:animate-fade-in var(--driver-animation-duration,.4s) ease-in-out}.driver-fade .driver-popover{animation:animate-fade-in var(--driver-animation-duration,.4s)}.driver-active-element-parent-no-scroll{overflow:hidden!important}.driver-no-interaction,.driver-no-interaction *{pointer-events:none!important}
|
|
@@ -63,12 +63,8 @@
|
|
|
63
63
|
@apply flex shrink-0 items-center gap-3 px-3 py-2.5;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
.mensa-table__export-dialog__delete-form {
|
|
67
|
-
@apply shrink-0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
66
|
.mensa-table__export-dialog__delete {
|
|
71
|
-
@apply inline-flex h-9 w-9 items-center justify-center rounded-md text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-red-600 dark:hover:text-red-400 focus:outline-none focus:ring-2 focus:ring-red-500/30 transition-colors cursor-pointer;
|
|
67
|
+
@apply shrink-0 inline-flex h-9 w-9 items-center justify-center rounded-md text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-red-600 dark:hover:text-red-400 focus:outline-none focus:ring-2 focus:ring-red-500/30 transition-colors cursor-pointer;
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
.mensa-table__export-dialog__item-info {
|
|
@@ -11,13 +11,27 @@ module Mensa
|
|
|
11
11
|
@filter = filter
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# A pill is a view filter only while it still filters like the view does,
|
|
15
|
+
# once the user changes it, it's their own filter.
|
|
14
16
|
def view_filter?
|
|
15
17
|
view = filter.table.table_view
|
|
16
18
|
return false unless view
|
|
17
19
|
|
|
18
20
|
view_filters = view.config&.dig(:filters) || view.config&.dig("filters") || {}
|
|
19
21
|
col = filter.column.name.to_s
|
|
20
|
-
view_filters
|
|
22
|
+
view_filter = view_filters[col] || view_filters[col.to_sym]
|
|
23
|
+
return false unless view_filter
|
|
24
|
+
|
|
25
|
+
view_filter = view_filter.to_h.with_indifferent_access
|
|
26
|
+
return false if view_filter[:operator].present? && view_filter[:operator].to_sym != filter.operator
|
|
27
|
+
|
|
28
|
+
comparable(view_filter[:value]) == comparable(filter.value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def comparable(value)
|
|
34
|
+
Array(value).map(&:to_s).reject(&:blank?).sort
|
|
21
35
|
end
|
|
22
36
|
end
|
|
23
37
|
end
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
<div class="mensa-table__search-bar__input-wrapper">
|
|
19
19
|
<input class="mensa-table__search-bar__input"
|
|
20
20
|
type="text"
|
|
21
|
-
name="search_query"
|
|
22
21
|
autocomplete="off"
|
|
23
22
|
placeholder="<%= t(has_filterable_columns ? ".search" : ".search_only") %>"
|
|
24
23
|
data-mensa-filter-pill-list-target="searchInput"
|
|
@@ -319,8 +319,11 @@ export default class FilterPillListComponentController extends ApplicationContro
|
|
|
319
319
|
const filtersDiffer =
|
|
320
320
|
filtersProvided &&
|
|
321
321
|
JSON.stringify(rendered) !== JSON.stringify(filters);
|
|
322
|
+
// The pills belong to the view the server rendered, so they only count
|
|
323
|
+
// when the stored view is that same view.
|
|
324
|
+
const viewDiffers = view.length > 0 && view !== this.renderedView();
|
|
322
325
|
const alreadyRendered =
|
|
323
|
-
Object.keys(rendered).length > 0 && !filtersDiffer;
|
|
326
|
+
Object.keys(rendered).length > 0 && !filtersDiffer && !viewDiffers;
|
|
324
327
|
|
|
325
328
|
if (!hasState || alreadyRendered || table.frameLoadHandled) {
|
|
326
329
|
if (typeof table.loadFrame === "function") {
|
|
@@ -546,6 +549,18 @@ export default class FilterPillListComponentController extends ApplicationContro
|
|
|
546
549
|
this._monitorResetButton();
|
|
547
550
|
}
|
|
548
551
|
|
|
552
|
+
// The id of the view checked in the views dropdown, "" without views.
|
|
553
|
+
renderedView() {
|
|
554
|
+
const root = this.element.closest(".mensa-table");
|
|
555
|
+
const check = root?.querySelector(
|
|
556
|
+
'[data-mensa-views-target="view"] .mensa-table__views__option-check:not(.invisible)',
|
|
557
|
+
);
|
|
558
|
+
return (
|
|
559
|
+
check?.closest('[data-mensa-views-target="view"]')?.dataset
|
|
560
|
+
.viewId || ""
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
|
|
549
564
|
setViewHighlight(view) {
|
|
550
565
|
if (!view) return;
|
|
551
566
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<% if table.current_user %>
|
|
2
2
|
<dialog class="mensa-table__save-view-dialog" data-mensa-table-target="saveViewDialog" data-action="click->mensa-table#saveViewDialogBackdrop">
|
|
3
|
-
<
|
|
3
|
+
<div class="mensa-table__save-view-dialog__form">
|
|
4
4
|
<header class="mensa-table__save-view-dialog__header">
|
|
5
5
|
<h2 class="mensa-table__save-view-dialog__title"><%= t(".save_view_title", default: "Save view") %></h2>
|
|
6
6
|
<p class="mensa-table__save-view-dialog__subtitle"><%= t(".save_view_subtitle", default: "Save the current filters, ordering and search as a reusable view.") %></p>
|
|
7
7
|
</header>
|
|
8
8
|
<label class="mensa-table__save-view-dialog__field">
|
|
9
9
|
<span class="mensa-table__save-view-dialog__label"><%= t(".view_name", default: "Name") %></span>
|
|
10
|
-
<input class="mensa-table__save-view-dialog__input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "e.g. Active customers") %>" data-mensa-table-target="saveViewName">
|
|
10
|
+
<input class="mensa-table__save-view-dialog__input" type="text" aria-required="true" placeholder="<%= t(".view_name_placeholder", default: "e.g. Active customers") %>" data-required-message="<%= t(".view_name_required", default: "Please enter a name for this view.") %>" data-mensa-table-target="saveViewName" data-action="keydown.enter->mensa-table#confirmSaveView input->mensa-table#clearValidity">
|
|
11
11
|
</label>
|
|
12
12
|
<label class="mensa-table__save-view-dialog__field">
|
|
13
13
|
<span class="mensa-table__save-view-dialog__label"><%= t(".view_description", default: "Description") %></span>
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
<button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--secondary" type="button" data-action="mensa-table#cancelSaveView">
|
|
18
18
|
<%= t(".cancel") %>
|
|
19
19
|
</button>
|
|
20
|
-
<button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--primary" type="
|
|
20
|
+
<button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--primary" type="button" data-action="mensa-table#confirmSaveView">
|
|
21
21
|
<%= t(".save") %>
|
|
22
22
|
</button>
|
|
23
23
|
</div>
|
|
24
|
-
</
|
|
24
|
+
</div>
|
|
25
25
|
</dialog>
|
|
26
26
|
<% end %>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ApplicationController from "mensa/controllers/application_controller";
|
|
2
|
-
import { get, post, patch } from "@rails/request.js";
|
|
2
|
+
import { get, post, patch, destroy } from "@rails/request.js";
|
|
3
3
|
|
|
4
4
|
export default class TableComponentController extends ApplicationController {
|
|
5
5
|
static targets = [
|
|
@@ -300,6 +300,8 @@ export default class TableComponentController extends ApplicationController {
|
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
async confirmSaveView(event) {
|
|
303
|
+
// Called from a button click or Enter in the name input. The table may
|
|
304
|
+
// sit inside a host <form>; this stops Enter from submitting it.
|
|
303
305
|
event.preventDefault();
|
|
304
306
|
|
|
305
307
|
const name = this.hasSaveViewNameTarget
|
|
@@ -307,7 +309,7 @@ export default class TableComponentController extends ApplicationController {
|
|
|
307
309
|
: "";
|
|
308
310
|
if (!name) {
|
|
309
311
|
if (this.hasSaveViewNameTarget)
|
|
310
|
-
this.saveViewNameTarget
|
|
312
|
+
this.reportMissing(this.saveViewNameTarget);
|
|
311
313
|
return;
|
|
312
314
|
}
|
|
313
315
|
|
|
@@ -560,28 +562,18 @@ export default class TableComponentController extends ApplicationController {
|
|
|
560
562
|
);
|
|
561
563
|
if (!options) return;
|
|
562
564
|
|
|
563
|
-
|
|
564
|
-
'input[name="repeat_mode"]:checked',
|
|
565
|
-
)?.value;
|
|
566
|
-
options.hidden = mode !== "repeating";
|
|
565
|
+
options.hidden = this._exportField("repeat_mode") !== "repeating";
|
|
567
566
|
}
|
|
568
567
|
|
|
569
568
|
confirmExport(event) {
|
|
570
|
-
event.preventDefault();
|
|
569
|
+
if (event) event.preventDefault();
|
|
571
570
|
if (!this.hasExportsUrlValue) return;
|
|
572
571
|
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
dialog.querySelector('input[name="scope"]:checked')?.value || "all";
|
|
576
|
-
const exportFormat =
|
|
577
|
-
dialog.querySelector('input[name="export_format"]:checked')
|
|
578
|
-
?.value || "csv_excel";
|
|
579
|
-
const repeatMode = dialog.querySelector(
|
|
580
|
-
'input[name="repeat_mode"]:checked',
|
|
581
|
-
)?.value;
|
|
572
|
+
const scope = this._exportField("scope") || "all";
|
|
573
|
+
const exportFormat = this._exportField("export_format") || "csv_excel";
|
|
582
574
|
const repeat =
|
|
583
|
-
|
|
584
|
-
?
|
|
575
|
+
this._exportField("repeat_mode") === "repeating"
|
|
576
|
+
? this._exportField("repeat") || ""
|
|
585
577
|
: "";
|
|
586
578
|
|
|
587
579
|
const state = this.currentViewState();
|
|
@@ -611,6 +603,27 @@ export default class TableComponentController extends ApplicationController {
|
|
|
611
603
|
});
|
|
612
604
|
}
|
|
613
605
|
|
|
606
|
+
deleteExport(event) {
|
|
607
|
+
if (event) event.preventDefault();
|
|
608
|
+
const url = event.currentTarget.dataset.url;
|
|
609
|
+
if (!url) return;
|
|
610
|
+
|
|
611
|
+
destroy(url, { responseKind: "turbo-stream" });
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Shows the browser's validation bubble for an empty text input. The
|
|
615
|
+
// inputs aren't `required` (that would block a host <form> around the
|
|
616
|
+
// table), so the message is set by hand and cleared again on input.
|
|
617
|
+
reportMissing(input) {
|
|
618
|
+
input.setCustomValidity(input.dataset.requiredMessage || "");
|
|
619
|
+
input.reportValidity();
|
|
620
|
+
input.focus();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
clearValidity(event) {
|
|
624
|
+
event.currentTarget.setCustomValidity("");
|
|
625
|
+
}
|
|
626
|
+
|
|
614
627
|
get ourUrl() {
|
|
615
628
|
if (
|
|
616
629
|
this.hasTurboFrameTarget &&
|
|
@@ -629,6 +642,17 @@ export default class TableComponentController extends ApplicationController {
|
|
|
629
642
|
|
|
630
643
|
// --- Private ---
|
|
631
644
|
|
|
645
|
+
// Export controls are found by data attribute: their names carry the
|
|
646
|
+
// table id so they don't group with radios elsewhere on the page.
|
|
647
|
+
_exportField(field) {
|
|
648
|
+
const dialog = this.exportDialogTarget;
|
|
649
|
+
const selector = `[data-mensa-export-field="${field}"]`;
|
|
650
|
+
const control =
|
|
651
|
+
dialog.querySelector(`input${selector}:checked`) ||
|
|
652
|
+
dialog.querySelector(`select${selector}`);
|
|
653
|
+
return control?.value;
|
|
654
|
+
}
|
|
655
|
+
|
|
632
656
|
// Reads the currently-selected view ID from the DOM (after Turbo re-renders
|
|
633
657
|
// the views component) and persists it to localStorage.
|
|
634
658
|
_persistSelectedViewId() {
|
|
@@ -674,7 +698,10 @@ export default class TableComponentController extends ApplicationController {
|
|
|
674
698
|
|
|
675
699
|
_openSaveDialog() {
|
|
676
700
|
if (!this.hasSaveViewDialogTarget) return;
|
|
677
|
-
if (this.hasSaveViewNameTarget)
|
|
701
|
+
if (this.hasSaveViewNameTarget) {
|
|
702
|
+
this.saveViewNameTarget.value = "";
|
|
703
|
+
this.saveViewNameTarget.setCustomValidity("");
|
|
704
|
+
}
|
|
678
705
|
if (this.hasSaveViewDescriptionTarget)
|
|
679
706
|
this.saveViewDescriptionTarget.value = "";
|
|
680
707
|
if (typeof this.saveViewDialogTarget.showModal === "function") {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/*! driver.js 1.10.1 | https://github.com/kamranahmedse/driver.js | MIT License | Copyright (c) Kamran Ahmed */
|
|
2
|
+
// Vendored from the driver.js npm package (dist/driver.js.mjs), unmodified. Keep in sync
|
|
3
|
+
// with app/assets/stylesheets/mensa/driver.css, which comes from the same version.
|
|
4
|
+
const e=[`pointerdown`,`mousedown`,`pointerup`,`mouseup`,`click`],t=new WeakMap;function n(n,i,a){r(n);let o=e=>{let t=e.target;n.contains(t)&&((!a||a(t))&&(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation()),e.type===`click`&&i?.(e))};for(let t of e)document.addEventListener(t,o,!0);t.set(n,o)}function r(n){let r=t.get(n);if(r){for(let t of e)document.removeEventListener(t,r,!0);t.delete(n)}}function i(e,t){let n=e.wrapper.getBoundingClientRect();return{width:n.width+t,height:n.height+t,realWidth:n.width,realHeight:n.height}}function a(e,t){let{elementDimensions:n,popoverDimensions:r,popoverPadding:i,popoverArrowDimensions:a}=t;return e===`start`?Math.max(Math.min(n.top-i,window.innerHeight-r.realHeight-a.width),a.width):e===`end`?Math.max(Math.min(n.top-r?.realHeight+n.height+i,window.innerHeight-r?.realHeight-a.width),a.width):e===`center`?Math.max(Math.min(n.top+n.height/2-r?.realHeight/2,window.innerHeight-r?.realHeight-a.width),a.width):0}function o(e,t){let{elementDimensions:n,popoverDimensions:r,popoverPadding:i,popoverArrowDimensions:a}=t;return e===`start`?Math.max(Math.min(n.left-i,window.innerWidth-r.realWidth-a.width),a.width):e===`end`?Math.max(Math.min(n.left-r?.realWidth+n.width+i,window.innerWidth-r?.realWidth-a.width),a.width):e===`center`?Math.max(Math.min(n.left+n.width/2-r?.realWidth/2,window.innerWidth-r?.realWidth-a.width),a.width):0}function s(e,t,n){let{align:r,side:s}=n,c=n.centered?`over`:s,l=n.padding,u=i(e,n.offset),f=e.arrow.getBoundingClientRect(),p=t.getBoundingClientRect(),m=p.top-u.height,h=m>=0,g=window.innerHeight-(p.bottom+u.height),_=g>=0,v=p.left-u.width,y=v>=0,b=window.innerWidth-(p.right+u.width),x=b>=0,S=!h&&!_&&!y&&!x,C=c;if(c===`top`&&h?x=y=_=!1:c===`bottom`&&_?x=y=h=!1:c===`left`&&y?x=h=_=!1:c===`right`&&x&&(y=h=_=!1),c===`over`){let t=window.innerWidth/2-u.realWidth/2,n=window.innerHeight/2-u.realHeight/2;e.wrapper.style.left=`${t}px`,e.wrapper.style.right=`auto`,e.wrapper.style.top=`${n}px`,e.wrapper.style.bottom=`auto`}else if(S){let t=window.innerWidth/2-u?.realWidth/2;e.wrapper.style.left=`${t}px`,e.wrapper.style.right=`auto`,e.wrapper.style.bottom=`10px`,e.wrapper.style.top=`auto`}else if(y){let t=Math.min(v,window.innerWidth-u?.realWidth-f.width),n=a(r,{elementDimensions:p,popoverDimensions:u,popoverPadding:l,popoverArrowDimensions:f});e.wrapper.style.left=`${t}px`,e.wrapper.style.top=`${n}px`,e.wrapper.style.bottom=`auto`,e.wrapper.style.right=`auto`,C=`left`}else if(x){let t=Math.min(b,window.innerWidth-u?.realWidth-f.width),n=a(r,{elementDimensions:p,popoverDimensions:u,popoverPadding:l,popoverArrowDimensions:f});e.wrapper.style.right=`${t}px`,e.wrapper.style.top=`${n}px`,e.wrapper.style.bottom=`auto`,e.wrapper.style.left=`auto`,C=`right`}else if(h){let t=Math.min(m,window.innerHeight-u.realHeight-f.width),n=o(r,{elementDimensions:p,popoverDimensions:u,popoverPadding:l,popoverArrowDimensions:f});e.wrapper.style.top=`${t}px`,e.wrapper.style.left=`${n}px`,e.wrapper.style.bottom=`auto`,e.wrapper.style.right=`auto`,C=`top`}else if(_){let t=Math.min(g,window.innerHeight-u?.realHeight-f.width),n=o(r,{elementDimensions:p,popoverDimensions:u,popoverPadding:l,popoverArrowDimensions:f});e.wrapper.style.left=`${n}px`,e.wrapper.style.bottom=`${t}px`,e.wrapper.style.top=`auto`,e.wrapper.style.right=`auto`,C=`bottom`}d(e,S?`over`:C,r,t),[...e.wrapper.classList].filter(e=>e.startsWith(`driver-popover-side-`)||e.startsWith(`driver-popover-align-`)).forEach(t=>e.wrapper.classList.remove(t)),e.wrapper.classList.add(`driver-popover-side-${C}`),e.wrapper.classList.add(`driver-popover-align-${r}`)}function c(e,t,n,r,i,a=10){let o=r-n;return e<=n&&t>=r?i===`start`?15+a/2:i===`end`?o-15-a/2:o/2:(Math.min(Math.max(e,n),r)+Math.min(Math.max(t,n),r))/2-n}function l(e,t,n=10){let r=t-15-n;if(r<15)return Math.max(0,(t-n)/2);let i=e-n/2;return Math.min(Math.max(i,15),r)}function u(e,t,n){return e===`left`||e===`right`?t.bottom>n.top&&t.top<n.bottom?e:t.bottom<=n.top?`bottom`:`top`:t.right>n.left&&t.left<n.right?e:t.right<=n.left?`right`:`left`}function d(e,t,n,r){let i=e.arrow;if(i.className=`driver-popover-arrow`,i.style.top=``,i.style.right=``,i.style.bottom=``,i.style.left=``,t===`over`){i.classList.add(`driver-popover-arrow-none`);return}let a=r.getBoundingClientRect(),o=e.wrapper.getBoundingClientRect(),s=u(t,a,o);i.classList.add(`driver-popover-arrow-side-${s}`);let d=i.getBoundingClientRect().width||10;if(s===`left`||s===`right`){let e=c(a.top,a.bottom,o.top,o.bottom,n,d);i.style.top=`${l(e,o.height,d)}px`}else{let e=c(a.left,a.right,o.left,o.right,n,d);i.style.left=`${l(e,o.width,d)}px`}}function f(e){return typeof e==`function`?e():typeof e==`string`?document.querySelector(e):e}function p(e){let t=window.getComputedStyle(e);return[t.overflow,t.overflowX,t.overflowY].some(e=>e===`auto`||e===`scroll`)}function m(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}function h(e){let t=`a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])`;return e.flatMap(e=>{let n=e.matches(t),r=Array.from(e.querySelectorAll(t));return[...n?[e]:[],...r]}).filter(e=>getComputedStyle(e).pointerEvents!==`none`&&y(e))}function g(e,t){if(!e||v(e))return;let n=e.offsetHeight>window.innerHeight;e.scrollIntoView({behavior:!t||_(e)?`auto`:`smooth`,inline:`center`,block:n?`start`:`center`})}function _(e){if(!e||!e.parentElement)return;let t=e.parentElement;return t.scrollHeight>t.clientHeight}function v(e){let t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)}function y(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)}function b(e){return e.closest(`dialog[open]`)??document.body}function x(e){e&&(e.wrapper.style.display=`none`)}function S(e,t){let r=w();b(e).appendChild(r.wrapper);let{title:i,description:a,showButtons:o,disableButtons:c,showProgress:l,nextBtnText:u,prevBtnText:d,progressText:f}=t;r.nextButton.innerHTML=u,r.previousButton.innerHTML=d,r.progress.innerHTML=f,r.closeButton.setAttribute(`aria-label`,t.closeBtnLabel),t.doneButton&&r.nextButton.classList.add(`driver-popover-done-btn`),i?(r.title.innerHTML=i,r.title.style.display=`block`):r.title.style.display=`none`,a?(r.description.innerHTML=a,r.description.style.display=`block`):r.description.style.display=`none`;let p=o.includes(`next`)||o.includes(`previous`)||l;r.closeButton.style.display=o.includes(`close`)?`block`:`none`,p?(r.footer.style.display=`flex`,r.progress.style.display=l?`block`:`none`,r.nextButton.style.display=o.includes(`next`)?`block`:`none`,r.previousButton.style.display=o.includes(`previous`)?`block`:`none`):r.footer.style.display=`none`,c.includes(`next`)&&(r.nextButton.disabled=!0,r.nextButton.classList.add(`driver-popover-btn-disabled`)),c.includes(`previous`)&&(r.previousButton.disabled=!0,r.previousButton.classList.add(`driver-popover-btn-disabled`)),c.includes(`close`)&&(r.closeButton.disabled=!0,r.closeButton.classList.add(`driver-popover-btn-disabled`));let m=r.wrapper;m.style.display=`block`,m.style.left=``,m.style.top=``,m.style.bottom=``,m.style.right=``,m.id=`driver-popover-content`,m.setAttribute(`role`,`dialog`),m.setAttribute(`aria-labelledby`,`driver-popover-title`),m.setAttribute(`aria-describedby`,`driver-popover-description`);let _=r.arrow;_.className=`driver-popover-arrow`,m.className=`driver-popover ${t.popoverClass||``}`.trim(),n(r.wrapper,e=>{let n=e.target instanceof Element?e.target:e.target?.parentElement;if(n){if(n.closest(`.driver-popover-next-btn`))return t.onNextClick?.();if(n.closest(`.driver-popover-prev-btn`))return t.onPrevClick?.();if(n.closest(`.driver-popover-close-btn`))return t.onCloseClick?.()}},e=>r.description.contains(e)||r.title.contains(e)?!1:!!e.closest(`.driver-popover-prev-btn, .driver-popover-next-btn, .driver-popover-close-btn`)),t.onRender?.(r),s(r,e,t.position),C(r,e,t.position),g(m,t.smoothScroll);let v=h([m,e]);return v.length>0&&v[0].focus(),r}function C(e,t,n){e.wrapper.querySelectorAll(`img`).forEach(r=>{if(r.complete)return;let i=()=>s(e,t,n);r.addEventListener(`load`,i,{once:!0}),r.addEventListener(`error`,i,{once:!0})})}function w(){let e=document.createElement(`div`);e.classList.add(`driver-popover`);let t=document.createElement(`div`);t.classList.add(`driver-popover-arrow`);let n=document.createElement(`header`);n.id=`driver-popover-title`,n.classList.add(`driver-popover-title`),n.style.display=`none`,n.innerText=`Popover Title`;let r=document.createElement(`div`);r.id=`driver-popover-description`,r.classList.add(`driver-popover-description`),r.style.display=`none`,r.innerText=`Popover description is here`;let i=document.createElement(`button`);i.type=`button`,i.classList.add(`driver-popover-close-btn`),i.innerHTML=`×`;let a=document.createElement(`footer`);a.classList.add(`driver-popover-footer`);let o=document.createElement(`span`);o.classList.add(`driver-popover-progress-text`),o.innerText=``;let s=document.createElement(`span`);s.classList.add(`driver-popover-navigation-btns`);let c=document.createElement(`button`);c.type=`button`,c.classList.add(`driver-popover-prev-btn`,`driver-popover-footer-btn`),c.innerHTML=`Previous`;let l=document.createElement(`button`);return l.type=`button`,l.classList.add(`driver-popover-next-btn`,`driver-popover-footer-btn`),l.innerHTML=`Next`,s.appendChild(c),s.appendChild(l),a.appendChild(o),a.appendChild(s),e.appendChild(i),e.appendChild(t),e.appendChild(n),e.appendChild(r),e.appendChild(a),{wrapper:e,arrow:t,title:n,description:r,footer:a,previousButton:c,nextButton:l,closeButton:i,footerButtons:s,progress:o}}function T(e){e&&(r(e.wrapper),e.wrapper.parentElement?.removeChild(e.wrapper))}function E(e,t){let n=window.innerWidth,r=window.innerHeight,i=t.padding,a=t.radius,o=e.width+i*2,s=e.height+i*2,c=Math.min(a,o/2,s/2),l=Math.floor(Math.max(c,0)),u=e.x-i+l,d=e.y-i,f=o-l*2,p=s-l*2;return`M${n},0L0,0L0,${r}L${n},${r}L${n},0Z
|
|
5
|
+
M${u},${d} h${f} a${l},${l} 0 0 1 ${l},${l} v${p} a${l},${l} 0 0 1 -${l},${l} h-${f} a${l},${l} 0 0 1 -${l},-${l} v-${p} a${l},${l} 0 0 1 ${l},-${l} z`}function D(e,t,n,r,i){let a=e.getState(`__activeStagePosition`),o=a||r.getBoundingClientRect(),s=i.getBoundingClientRect();a={x:m(t,o.x,s.x-o.x,n),y:m(t,o.y,s.y-o.y,n),width:m(t,o.width,s.width-o.width,n),height:m(t,o.height,s.height-o.height,n)},k(e,a),e.setState(`__activeStagePosition`,a)}function O(e,t){if(!t)return;let n=t.getBoundingClientRect(),r={x:n.x,y:n.y,width:n.width,height:n.height};e.setState(`__activeStagePosition`,r),k(e,r)}function ee(e){let t=e.getState(`__activeStagePosition`),n=e.getState(`__overlaySvg`);if(!t)return;if(!n){console.warn(`No stage svg found.`);return}let r=window.innerWidth,i=window.innerHeight;n.setAttribute(`viewBox`,`0 0 ${r} ${i}`)}function te(e,t){let r=j(e,t);document.body.appendChild(r),n(r,t=>{t.target.tagName===`path`&&e.emit(`overlayClick`)}),e.setState(`__overlaySvg`,r)}function k(e,t){let n=e.getState(`__overlaySvg`);if(!n){te(e,t);return}let r=n.firstElementChild;if(r?.tagName!==`path`)throw Error(`no path element found in stage svg`);r.setAttribute(`d`,E(t,A(e)))}function A(e){return e.getState(`activeElement`)?.id===`driver-dummy-element`?{padding:0,radius:0}:{padding:e.getConfig(`stagePadding`)||0,radius:e.getConfig(`stageRadius`)||0}}function j(e,t){let n=window.innerWidth,r=window.innerHeight,i=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);i.classList.add(`driver-overlay`,`driver-overlay-animated`),i.setAttribute(`viewBox`,`0 0 ${n} ${r}`),i.setAttribute(`xmlSpace`,`preserve`),i.setAttribute(`xmlnsXlink`,`http://www.w3.org/1999/xlink`),i.setAttribute(`version`,`1.1`),i.setAttribute(`preserveAspectRatio`,`xMinYMin slice`),i.style.fillRule=`evenodd`,i.style.clipRule=`evenodd`,i.style.strokeLinejoin=`round`,i.style.strokeMiterlimit=`2`,i.style.zIndex=`10000`,i.style.position=`fixed`,i.style.top=`0`,i.style.left=`0`,i.style.width=`100%`,i.style.height=`100%`;let a=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return a.setAttribute(`d`,E(t,A(e))),a.style.fill=e.getConfig(`overlayColor`)||`rgb(0,0,0)`,a.style.opacity=`${e.getConfig(`overlayOpacity`)}`,a.style.pointerEvents=`auto`,a.style.cursor=`auto`,i.appendChild(a),i}function M(e){let t=e.getState(`__overlaySvg`);t&&(r(t),t.remove())}const N=`{{current}} of {{total}}`;function P(e,t){return!(t.skipMissingElement??e.getConfig(`skipMissingElement`))||!t.element?!1:!f(t.element)}function F(e,t,n){let r=e.getConfig(`steps`)||[];for(let i=t;i>=0&&i<r.length;i+=n)if(!P(e,r[i]))return i}function I(e,t){let n=e.getState(`activeIndex`),r=n!==void 0&&F(e,n+1,1)===void 0,i=t?.popover?.onDoneClick||e.getConfig(`onDoneClick`);return r&&i?i:t?.popover?.onNextClick||e.getConfig(`onNextClick`)}function L(e,t){return t?.popover?.onPrevClick||e.getConfig(`onPrevClick`)}function R(e,t){return t?.popover?.onCloseClick||e.getConfig(`onCloseClick`)}function z(e,t,n){let r=e.getConfig(`steps`)||[],i=r[t],a=i.popover||{},o=F(e,t+1,1)!==void 0,s=F(e,t-1,-1)!==void 0,c=a.doneBtnText||e.getConfig(`doneBtnText`)||`Done`,l=e.getConfig(`allowClose`),u=a.showProgress===void 0?e.getConfig(`showProgress`):a.showProgress,d=(a.progressText||e.getConfig(`progressText`)||N).replace(`{{current}}`,`${t+1}`).replace(`{{total}}`,`${r.length}`),f=a.showButtons||e.getConfig(`showButtons`),p=[`next`,`previous`,...l?[`close`]:[]].filter(e=>!f?.length||f.includes(e)),m=a.onNextClick||e.getConfig(`onNextClick`),h=a.onPrevClick||e.getConfig(`onPrevClick`),g=a.onCloseClick||e.getConfig(`onCloseClick`);return{...i,popover:{showButtons:p,nextBtnText:o?void 0:c,disableButtons:[...e.getConfig(`disableButtons`)||[],...s?[]:[`previous`]],showProgress:u,onNextClick:m||n.onNextClick,onPrevClick:h||n.onPrevClick,onCloseClick:g||n.onCloseClick,...a,progressText:d}}}function B(e,t){return{showButtons:t?.popover?.showButtons||e.getConfig(`showButtons`)||[],disableButtons:t?.popover?.disableButtons||e.getConfig(`disableButtons`)||[]}}function V(e,t,n){let r=e.getConfig(`stagePadding`)||0;return{side:n.popover?.side||`bottom`,align:n.popover?.align||`start`,offset:r+(e.getConfig(`popoverOffset`)||0),padding:r,centered:t.id===`driver-dummy-element`}}function H(e,t,n){let r=n.popover||{},i=e.getState(`activeIndex`),a=i!==void 0&&F(e,i+1,1)===void 0;return{title:r.title,description:r.description,...B(e,n),showProgress:r.showProgress||e.getConfig(`showProgress`)||!1,progressText:r.progressText??(e.getConfig(`progressText`)||N),nextBtnText:r.nextBtnText??(e.getConfig(`nextBtnText`)||`Next`),prevBtnText:r.prevBtnText??(e.getConfig(`prevBtnText`)||`Previous`),closeBtnLabel:r.closeBtnLabel??(e.getConfig(`closeBtnLabel`)||`Close`),doneButton:a,popoverClass:r.popoverClass||e.getConfig(`popoverClass`)||``,smoothScroll:e.getConfig(`smoothScroll`),onNextClick:()=>{a&&e.setState(`__destroyReason`,`done`);let r=I(e,n);return r?r(t,n,e.getHookOpts()):e.emit(`nextClick`)},onPrevClick:()=>{let r=L(e,n);return r?r(t,n,e.getHookOpts()):e.emit(`prevClick`)},onCloseClick:()=>{e.setState(`__destroyReason`,`close`);let r=R(e,n);return r?r(t,n,e.getHookOpts()):e.emit(`closeClick`)},onRender:t=>{e.setState(`popover`,t),(r.onPopoverRender||e.getConfig(`onPopoverRender`))?.(t,e.getHookOpts())},position:V(e,t,n)}}function U(e,t,n){T(e.getState(`popover`)),S(t,H(e,t,n))}function W(e,t,n){let r=e.getState(`popover`);r&&s(r,t,V(e,t,n))}const G={"aria-haspopup":`dialog`,"aria-expanded":`true`,"aria-controls":`driver-popover-content`},K=new WeakMap;function q(e){if(!K.has(e)){let t=Object.fromEntries(Object.keys(G).map(t=>[t,e.getAttribute(t)]));K.set(e,t)}for(let[t,n]of Object.entries(G))e.setAttribute(t,n)}function J(e){let t=K.get(e);if(t){for(let[n,r]of Object.entries(t))r===null?e.removeAttribute(n):e.setAttribute(n,r);K.delete(e)}}function Y(){let e=document.getElementById(`driver-dummy-element`);if(e)return e;let t=document.createElement(`div`);return t.id=`driver-dummy-element`,t.style.width=`0`,t.style.height=`0`,t.style.pointerEvents=`none`,t.style.opacity=`0`,t.style.position=`fixed`,t.style.top=`50%`,t.style.left=`50%`,document.body.appendChild(t),t}function X(e,t){let n=f(t.element);n||(n=Y()),ne(e,n,t)}function Z(e){let t=e.getState(`__activeElement`),n=e.getState(`__activeStep`);t&&(O(e,t),ee(e),W(e,t,n))}function ne(e,t,n){let r=e.getConfig(`duration`)||400,i=Date.now(),a=e.getState(`__activeStep`),o=e.getState(`__activeElement`)||t,s=!o||o===t,c=t.id===`driver-dummy-element`,l=o.id===`driver-dummy-element`,u=e.getConfig(`animate`),d=n.onHighlightStarted||e.getConfig(`onHighlightStarted`),f=n?.onHighlighted||e.getConfig(`onHighlighted`),m=a?.onDeselected||e.getConfig(`onDeselected`),h=e.getHookOpts();a&&a!==n&&m&&m(l?void 0:o,a,h),d&&d(c?void 0:t,n,h);let _=!s&&u,v=!1;x(e.getState(`popover`)),e.setState(`previousStep`,a),e.setState(`previousElement`,o),e.setState(`activeStep`,n),e.setState(`activeElement`,t);let y=()=>{if(e.getState(`__transitionCallback`)!==y)return;let s=Date.now()-i,l=r-s<=r/2;n.popover&&l&&!v&&_&&(U(e,t,n),v=!0),e.getConfig(`animate`)&&s<r?D(e,s,r,o,t):(O(e,t),f&&f(c?void 0:t,n,e.getHookOpts()),e.setState(`__transitionCallback`,void 0),e.setState(`__previousStep`,a),e.setState(`__previousElement`,o),e.setState(`__activeStep`,n),e.setState(`__activeElement`,t)),window.requestAnimationFrame(y)};e.setState(`__transitionCallback`,y),window.requestAnimationFrame(y),g(t,e.getConfig(`smoothScroll`)),!_&&n.popover&&U(e,t,n),document.querySelectorAll(`.driver-active-element-parent`).forEach(e=>{e.classList.remove(`driver-active-element-parent`,`driver-active-element-parent-no-scroll`)}),document.querySelectorAll(`.driver-active-element`).forEach(e=>{e.classList.remove(`driver-active-element`,`driver-no-interaction`),J(e)}),(n.disableActiveInteraction??e.getConfig(`disableActiveInteraction`))&&t.classList.add(`driver-no-interaction`);let b=t.parentElement;b&&b!==document.body&&(b.classList.add(`driver-active-element-parent`),p(b)&&b.classList.add(`driver-active-element-parent-no-scroll`)),t.classList.add(`driver-active-element`),q(t)}function re(){document.getElementById(`driver-dummy-element`)?.remove(),document.querySelectorAll(`.driver-active-element`).forEach(e=>{let t=e.parentElement;t&&t!==document.body&&t.classList.remove(`driver-active-element-parent`,`driver-active-element-parent-no-scroll`),e.classList.remove(`driver-active-element`,`driver-no-interaction`),J(e)})}function Q(e){let t=e.getState(`__resizeTimeout`);t&&window.cancelAnimationFrame(t),e.setState(`__resizeTimeout`,window.requestAnimationFrame(()=>Z(e)))}function ie(e,t){if(!e.getState(`isInitialized`)||!(t.key===`Tab`||t.keyCode===9))return;let n=e.getState(`__activeElement`),r=e.getState(`popover`)?.wrapper,i=h([...r?[r]:[],...n?[n]:[]]),a=i[0],o=i[i.length-1];t.preventDefault(),t.shiftKey?(i[i.indexOf(document.activeElement)-1]||o)?.focus():(i[i.indexOf(document.activeElement)+1]||a)?.focus()}function ae(e,t){(e.getConfig(`allowKeyboardControl`)??!0)&&(t.key===`Escape`?e.emit(`escapePress`):t.key===`ArrowRight`?e.emit(`arrowRightPress`):t.key===`ArrowLeft`&&e.emit(`arrowLeftPress`))}function oe(e,t){let n=e.getState(`__activeElement`),r=t.target;!n||!r||!n.contains(r)||e.emit(`activeElementClick`)}function se(e){let t=t=>ae(e,t),n=t=>ie(e,t),r=()=>Q(e),i=()=>Q(e),a=t=>oe(e,t);e.setState(`__events`,{onKeyup:t,onKeydown:n,onResize:r,onScroll:i,onClick:a}),window.addEventListener(`keyup`,t,!1),window.addEventListener(`keydown`,n,!1),window.addEventListener(`resize`,r),window.addEventListener(`scroll`,i),document.addEventListener(`click`,a,!1)}function ce(e){let t=e.getState(`__events`);t&&(window.removeEventListener(`keyup`,t.onKeyup),window.removeEventListener(`keydown`,t.onKeydown),window.removeEventListener(`resize`,t.onResize),window.removeEventListener(`scroll`,t.onScroll),document.removeEventListener(`click`,t.onClick,!1))}function le(){let e={};function t(t={}){e={animate:!0,duration:400,allowClose:!0,allowScroll:!0,overlayClickBehavior:`close`,overlayOpacity:.7,smoothScroll:!1,disableActiveInteraction:!1,advanceOnClick:!1,skipMissingElement:!1,waitForElement:0,showProgress:!1,stagePadding:10,stageRadius:5,popoverOffset:10,showButtons:[`next`,`previous`,`close`],disableButtons:[],overlayColor:`#000`,...t}}return t(),{getConfig:(t=>t?e[t]:e),configure:t}}function ue(){let e={},t=(t=>t?e[t]:e),n=(t,n)=>{e[t]=n};function r(){e={}}return{getState:t,setState:n,resetState:r}}function de(){let e={};function t(t,n){e[t]=n}function n(t){e[t]?.()}function r(){e={}}return{listen:t,emit:n,reset:r}}function fe(e={}){let t=le();t.configure(e);let n=ue(),r=de(),i;return{getConfig:t.getConfig,setConfig:t.configure,getState:n.getState,setState:n.setState,resetState:n.resetState,listen:r.listen,emit:r.emit,resetEmitter:r.reset,getDriver:()=>i,setDriver:e=>{i=e},getHookOpts:(e,r)=>{let a=e||n.getState();return{config:t.getConfig(),state:a,driver:i,index:a.activeIndex,...r?{reason:r}:{}}}}}let $;function pe(e={}){let t=fe(e),n=()=>y(`replaced`,!1);function r(e=`close`){t.getConfig(`allowClose`)&&y(e)}function i(){let e=t.getState(`activeIndex`);e!==void 0&&F(t,e+1,1)===void 0&&t.setState(`__destroyReason`,`done`)}function a(){let{disableButtons:e}=B(t,t.getState(`__activeStep`));e.includes(`close`)||r(`escape`)}function o(e){let{showButtons:n,disableButtons:r}=B(t,t.getState(`__activeStep`));return n.includes(e)&&!r.includes(e)}function s(){let e=t.getConfig(`overlayClickBehavior`);if(t.getConfig(`allowClose`)&&e===`close`){y(`overlay`);return}if(typeof e==`function`){t.setState(`__destroyReason`,`overlay`);let n=t.getState(`__activeStep`);e(t.getState(`__activeElement`),n,t.getHookOpts());return}if(e===`nextStep`){let e=t.getState(`activeStep`),n=t.getState(`activeElement`),r=I(t,e);if(r){i(),r(n,e,t.getHookOpts());return}c()}}function c(){let e=t.getState(`activeIndex`),n=t.getConfig(`steps`)||[];if(e===void 0)return;let r=e+1;n[r]?v(r):y(`done`)}function l(){let e=t.getState(`activeIndex`),n=t.getConfig(`steps`)||[];if(e===void 0)return;let r=e-1;n[r]?v(r):y(`api`)}function u(e){(t.getConfig(`steps`)||[])[e]?v(e):y(`api`)}function d(){if(t.getState(`__transitionCallback`))return;let e=t.getState(`__activeStep`);if(!e||!(e.advanceOnClick??t.getConfig(`advanceOnClick`)))return;let n=t.getState(`__activeElement`),r=I(t,e);if(r){i(),r(n,e,t.getHookOpts());return}c()}function p(){if(t.getState(`__transitionCallback`))return;let e=t.getState(`activeIndex`),n=t.getState(`__activeStep`),r=t.getState(`__activeElement`);if(e===void 0||n===void 0||!(t.getConfig(`steps`)||[])[e-1]||!o(`previous`))return;let i=L(t,n);if(i)return i(r,n,t.getHookOpts());l()}function m(){if(t.getState(`__transitionCallback`))return;let e=t.getState(`activeIndex`),n=t.getState(`__activeStep`),r=t.getState(`__activeElement`);if(e===void 0||n===void 0||!o(`next`))return;let a=I(t,n);if(a)return i(),a(r,n,t.getHookOpts());c()}function h(){t.getState(`isInitialized`)||($&&$!==n&&$(),$=n,t.setState(`isInitialized`,!0),document.body.classList.add(`driver-active`,t.getConfig(`animate`)?`driver-fade`:`driver-simple`),t.getConfig(`allowScroll`)||document.body.classList.add(`driver-no-scroll`),document.body.style.setProperty(`--driver-animation-duration`,`${t.getConfig(`duration`)||400}ms`),se(t),t.listen(`overlayClick`,s),t.listen(`activeElementClick`,d),t.listen(`escapePress`,a),t.listen(`closeClick`,r),t.listen(`arrowLeftPress`,p),t.listen(`arrowRightPress`,m))}function g(){let e=t.getState(`__pendingWaitCancel`);e&&(t.setState(`__pendingWaitCancel`,void 0),e())}function _(e,n,r){let i=()=>{a.disconnect(),window.clearTimeout(o),t.setState(`__pendingWaitCancel`,void 0),r()},a=new MutationObserver(()=>{f(e.element)&&i()}),o=window.setTimeout(i,n);t.setState(`__pendingWaitCancel`,()=>{a.disconnect(),window.clearTimeout(o)}),a.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!0})}function v(e=0,n=!1){g(),t.setState(`__destroyReason`,void 0);let r=t.getConfig(`steps`);if(!r){console.error(`No steps to drive through`),y(`api`);return}if(!r[e]){y(`api`);return}let i=r[e],a=i.waitForElement??t.getConfig(`waitForElement`)??0;if(!n&&a>0&&i.element&&!f(i.element)){x(t.getState(`popover`)),_(i,a,()=>v(e,!0));return}if(P(t,i)){let n=t.getState(`activeIndex`),i=typeof n==`number`&&e<n?-1:1;r[e+i]?v(e+i):i===1&&y(`done`);return}t.setState(`__activeOnDestroyed`,document.activeElement),t.setState(`activeIndex`,e);let o=r[e+1];X(t,z(t,e,{onNextClick:()=>{o?v(e+1):y(`done`)},onPrevClick:()=>{v(e-1)},onCloseClick:()=>{y(`close`)}}))}function y(e,r=!0){if(!t.getState(`isInitialized`))return;e===`api`&&(e=t.getState(`__destroyReason`)??`api`);let i=t.getState(`__activeElement`),a=t.getState(`__activeStep`),o=t.getState(`__activeOnDestroyed`),s=t.getConfig(`onDestroyStarted`);if(r&&s){t.setState(`__destroyReason`,e),s(!i||i?.id===`driver-dummy-element`?void 0:i,a,t.getHookOpts(void 0,e));return}$===n&&($=void 0);let c=a?.onDeselected||t.getConfig(`onDeselected`),l=t.getConfig(`onDestroyed`);document.body.classList.remove(`driver-active`,`driver-fade`,`driver-simple`,`driver-no-scroll`),document.body.style.removeProperty(`--driver-animation-duration`),g(),ce(t),T(t.getState(`popover`)),re(),M(t),t.resetEmitter();let u=t.getState();if(t.resetState(),i&&a){let n=i.id===`driver-dummy-element`;c&&c(n?void 0:i,a,t.getHookOpts(u,e)),l&&l(n?void 0:i,a,t.getHookOpts(u,e))}o&&o.focus()}let b={isActive:()=>t.getState(`isInitialized`)||!1,refresh:()=>Q(t),drive:(e=0)=>{h(),v(e)},setConfig:t.setConfig,setSteps:e=>{g(),t.resetState(),t.setConfig({...t.getConfig(),steps:e})},getConfig:t.getConfig,getState:t.getState,getActiveIndex:()=>t.getState(`activeIndex`),isFirstStep:()=>{let e=t.getState(`activeIndex`);return e!==void 0&&F(t,e-1,-1)===void 0},isLastStep:()=>{let e=t.getState(`activeIndex`);return e!==void 0&&F(t,e+1,1)===void 0},getActiveStep:()=>t.getState(`activeStep`),getActiveElement:()=>t.getState(`activeElement`),getPreviousElement:()=>t.getState(`previousElement`),getPreviousStep:()=>t.getState(`previousStep`),getNextStep:()=>{let e=t.getConfig(`steps`)||[],n=t.getState(`activeIndex`);if(n===void 0)return;let r=F(t,n+1,1);return r===void 0?void 0:e[r]},moveNext:c,movePrevious:l,moveTo:u,hasNextStep:()=>{let e=t.getState(`activeIndex`);return e!==void 0&&F(t,e+1,1)!==void 0},hasPreviousStep:()=>{let e=t.getState(`activeIndex`);return e!==void 0&&F(t,e-1,-1)!==void 0},highlight:e=>{h(),t.setState(`__destroyReason`,void 0),X(t,{...e,popover:e.popover?{showButtons:[],showProgress:!1,progressText:``,...e.popover}:void 0})},destroy:()=>{y(`api`,!1)}};return t.setDriver(b),b}export{pe as driver};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* Dark mode for the driver.js tour popover. driver.css is vendored unmodified, so the
|
|
2
|
+
overrides live here and must be imported after it. */
|
|
3
|
+
.driver-popover {
|
|
4
|
+
@apply dark:bg-gray-800 dark:text-gray-100 dark:ring-1 dark:ring-white/10;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.driver-popover-description {
|
|
8
|
+
@apply dark:text-gray-300;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.driver-popover-progress-text {
|
|
12
|
+
@apply dark:text-gray-400;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.driver-popover-close-btn {
|
|
16
|
+
@apply dark:text-gray-500 dark:hover:text-gray-200 dark:focus:text-gray-200;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.driver-popover-footer button {
|
|
20
|
+
@apply dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600 dark:[text-shadow:none] dark:hover:bg-gray-600 dark:focus:bg-gray-600;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* The arrow is a border triangle: only the side pointing at the element is coloured,
|
|
24
|
+
the other three stay transparent. */
|
|
25
|
+
.driver-popover-arrow-side-left {
|
|
26
|
+
@apply dark:border-l-gray-800;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.driver-popover-arrow-side-right {
|
|
30
|
+
@apply dark:border-r-gray-800;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.driver-popover-arrow-side-top {
|
|
34
|
+
@apply dark:border-t-gray-800;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.driver-popover-arrow-side-bottom {
|
|
38
|
+
@apply dark:border-b-gray-800;
|
|
39
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ApplicationController from "mensa/controllers/application_controller";
|
|
2
|
-
import { driver } from "driver
|
|
2
|
+
import { driver } from "mensa/components/table/driver";
|
|
3
3
|
|
|
4
4
|
// Explains the table's features the first time each one is seen. Every feature is
|
|
5
5
|
// its own segment, remembered in localStorage, so a feature missing on one table is
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.mensa-table__views__dropdown {
|
|
14
|
-
@apply
|
|
14
|
+
@apply fixed z-50 min-w-[12rem] rounded-lg bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black/5 dark:ring-white/10 py-1;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.mensa-table__views__option {
|
|
@@ -55,21 +55,21 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
|
|
57
57
|
<dialog class="mensa-table__views__rename-dialog" data-mensa-views-target="renameDialog" data-action="click->mensa-views#renameDialogBackdrop">
|
|
58
|
-
<
|
|
58
|
+
<div class="mensa-table__views__rename-form">
|
|
59
59
|
<input type="hidden" data-mensa-views-target="renameViewId">
|
|
60
60
|
<h3 class="mensa-table__views__rename-title"><%= t(".rename_view", default: "Rename view") %></h3>
|
|
61
61
|
<label class="mensa-table__views__rename-label">
|
|
62
62
|
<%= t(".view_name", default: "Name") %>
|
|
63
|
-
<input class="mensa-table__views__rename-input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "View name") %>" data-mensa-views-target="renameInput">
|
|
63
|
+
<input class="mensa-table__views__rename-input" type="text" aria-required="true" placeholder="<%= t(".view_name_placeholder", default: "View name") %>" data-required-message="<%= t(".view_name_required", default: "Please enter a name for this view.") %>" data-mensa-views-target="renameInput" data-action="keydown.enter->mensa-views#confirmRename input->mensa-views#clearRenameValidity">
|
|
64
64
|
</label>
|
|
65
65
|
<div class="mensa-table__views__rename-actions">
|
|
66
66
|
<button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--secondary" type="button" data-action="mensa-views#cancelRename">
|
|
67
67
|
<%= t(".cancel", default: "Cancel") %>
|
|
68
68
|
</button>
|
|
69
|
-
<button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--primary" type="
|
|
69
|
+
<button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--primary" type="button" data-action="mensa-views#confirmRename">
|
|
70
70
|
<%= t(".rename", default: "Rename") %>
|
|
71
71
|
</button>
|
|
72
72
|
</div>
|
|
73
|
-
</
|
|
73
|
+
</div>
|
|
74
74
|
</dialog>
|
|
75
75
|
</div>
|
|
@@ -90,6 +90,7 @@ export default class ViewsComponentController extends ApplicationController {
|
|
|
90
90
|
if (this.hasRenameDialogTarget) {
|
|
91
91
|
this.renameViewIdTarget.value = viewId;
|
|
92
92
|
this.renameInputTarget.value = viewName;
|
|
93
|
+
this.renameInputTarget.setCustomValidity("");
|
|
93
94
|
if (typeof this.renameDialogTarget.showModal === "function") {
|
|
94
95
|
this.renameDialogTarget.showModal();
|
|
95
96
|
} else {
|
|
@@ -111,11 +112,18 @@ export default class ViewsComponentController extends ApplicationController {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
async confirmRename(event) {
|
|
115
|
+
// Called from a button click or Enter in the name input. The table may
|
|
116
|
+
// sit inside a host <form>; this stops Enter from submitting it.
|
|
114
117
|
event.preventDefault();
|
|
115
118
|
const viewId = this.renameViewIdTarget.value;
|
|
116
119
|
const newName = this.renameInputTarget.value.trim();
|
|
117
120
|
if (!newName) {
|
|
118
|
-
|
|
121
|
+
// The input isn't `required` (that would block a host <form>), so
|
|
122
|
+
// the browser's message is set by hand and cleared on input.
|
|
123
|
+
const input = this.renameInputTarget;
|
|
124
|
+
input.setCustomValidity(input.dataset.requiredMessage || "");
|
|
125
|
+
input.reportValidity();
|
|
126
|
+
input.focus();
|
|
119
127
|
return;
|
|
120
128
|
}
|
|
121
129
|
|
|
@@ -131,6 +139,10 @@ export default class ViewsComponentController extends ApplicationController {
|
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
clearRenameValidity() {
|
|
143
|
+
this.renameInputTarget.setCustomValidity("");
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
async duplicateView(event) {
|
|
135
147
|
event.preventDefault();
|
|
136
148
|
const viewId = this.submenuTarget.dataset.viewId;
|
|
@@ -198,10 +210,19 @@ export default class ViewsComponentController extends ApplicationController {
|
|
|
198
210
|
// --- private ---
|
|
199
211
|
|
|
200
212
|
_openDropdown() {
|
|
213
|
+
this._positionDropdown();
|
|
201
214
|
this.dropdownTarget.classList.remove("hidden");
|
|
202
215
|
this._bindOutsideClick();
|
|
203
216
|
}
|
|
204
217
|
|
|
218
|
+
// Fixed positioning with viewport coordinates, so a small table (or a host
|
|
219
|
+
// element with overflow hidden) doesn't clip the dropdown.
|
|
220
|
+
_positionDropdown() {
|
|
221
|
+
const rect = this.triggerTarget.getBoundingClientRect();
|
|
222
|
+
this.dropdownTarget.style.top = `${rect.bottom + 4}px`;
|
|
223
|
+
this.dropdownTarget.style.left = `${rect.left}px`;
|
|
224
|
+
}
|
|
225
|
+
|
|
205
226
|
_closeDropdown() {
|
|
206
227
|
this.dropdownTarget.classList.add("hidden");
|
|
207
228
|
this._closeSubmenu();
|
data/app/tables/mensa/group.rb
CHANGED
|
@@ -34,6 +34,9 @@ module Mensa
|
|
|
34
34
|
return unless aggregates.key?(column.name)
|
|
35
35
|
|
|
36
36
|
value = aggregates[column.name]
|
|
37
|
+
# SUM of a bigint column comes back as a BigDecimal, which would show as
|
|
38
|
+
# "3,000.0". Whole numbers are shown without decimals.
|
|
39
|
+
value = value.to_i if value.is_a?(BigDecimal) && value.frac.zero?
|
|
37
40
|
return ActiveSupport::NumberHelper.number_to_delimited(value) if value.is_a?(Numeric)
|
|
38
41
|
|
|
39
42
|
Mensa::Cell.new(row: nil, column: column, value: value).formatted
|