mensa 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae217694f2f5b6da4894f8298dcce685b65b7801db66ff34b6e3adc4fe5450ac
4
- data.tar.gz: e0df29185e90dba9d131204ea3750653d7ebf01cb0a26eb4e423fef9e1999f15
3
+ metadata.gz: e65b5449f34142c241ec808818ae9634e246c73b1b6b6f17e794b998378baf29
4
+ data.tar.gz: b4e1be79e6941ed67ab28d5b9188246398d332166ca225dcf6b53b936faad403
5
5
  SHA512:
6
- metadata.gz: 5e51ed2fd1e658224721238213d654ef55aa99913b409de4baee537206497c742e45b6f5f63d03788c8e35fd1cd2c14594d2c6529e1b75593c278010ecc20c5d
7
- data.tar.gz: '09dea0710ed582ead8878a6bd07caabcf17cbd84fa883f388bf8d94297ad4fc3bbe48a83b1c6ed4db507b37802432f0ee773d6e2514f07d5bebb6402e5fb41fa'
6
+ metadata.gz: 8bc4602e03b60c11998a8aa470476457aff5533b8a2c8936b049174e2ee2d017318c7ddd6969b8a6194aa59c643cf1db52afd2b53ba56e856619ee701bc1cb46
7
+ data.tar.gz: b43c3400f65e2c1e1a32b30a0d043e82f9df71c3800485d4f3e4201dd18789f525a43018232c003034205e6460eee538583aa26286ea4c502debbee0ba9ad216
data/.shipkit.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  ---
2
- previous_version: 0.8.0
2
+ previous_version: 0.8.2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mensa (0.8.0)
4
+ mensa (0.8.2)
5
5
  csv
6
6
  importmap-rails
7
7
  pagy (>= 43)
@@ -1,3 +1,4 @@
1
+ @import "driver.css";
1
2
  @import "../../../components/mensa/add_filter/component.css";
2
3
  @import "../../../components/mensa/column_customizer/component.css";
3
4
  @import "../../../components/mensa/cell/component.css";
@@ -0,0 +1 @@
1
+ .driver-active .driver-overlay,.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 .2s ease-in-out}.driver-fade .driver-popover{animation:animate-fade-in .2s}.driver-popover{all:unset;box-sizing:border-box;color:#2d2d2d;margin:0;padding:15px;border-radius:5px;min-width:250px;max-width:300px;box-shadow:0 1px 10px #0006;z-index:1000000000;position:fixed;top:0;right:0;background-color:#fff}.driver-popover *{font-family:Helvetica Neue,Inter,ui-sans-serif,"Apple Color Emoji",Helvetica,Arial,sans-serif}.driver-popover-title{font:19px/normal sans-serif;font-weight:700;display:block;position:relative;line-height:1.5;zoom:1;margin:0}.driver-popover-close-btn{all:unset;position:absolute;top:0;right:0;width:32px;height:28px;cursor:pointer;font-size:18px;font-weight:500;color:#d2d2d2;z-index:1;text-align:center;transition:color;transition-duration:.2s}.driver-popover-close-btn:hover,.driver-popover-close-btn:focus{color:#2d2d2d}.driver-popover-title[style*=block]+.driver-popover-description{margin-top:5px}.driver-popover-description{margin-bottom:0;font:14px/normal sans-serif;line-height:1.5;font-weight:400;zoom:1}.driver-popover-footer{margin-top:15px;text-align:right;zoom:1;display:flex;align-items:center;justify-content:space-between}.driver-popover-progress-text{font-size:13px;font-weight:400;color:#727272;zoom:1}.driver-popover-footer button{all:unset;display:inline-block;box-sizing:border-box;padding:3px 7px;text-decoration:none;text-shadow:1px 1px 0 #fff;background-color:#fff;color:#2d2d2d;font:12px/normal sans-serif;cursor:pointer;outline:0;zoom:1;line-height:1.3;border:1px solid #ccc;border-radius:3px}.driver-popover-footer .driver-popover-btn-disabled{opacity:.5;pointer-events:none}:not(body):has(>.driver-active-element){overflow:hidden!important}.driver-no-interaction,.driver-no-interaction *{pointer-events:none!important}.driver-popover-footer button:hover,.driver-popover-footer button:focus{background-color:#f7f7f7}.driver-popover-navigation-btns{display:flex;flex-grow:1;justify-content:flex-end}.driver-popover-navigation-btns button+button{margin-left:4px}.driver-popover-arrow{content:"";position:absolute;border:5px solid #fff}.driver-popover-arrow-side-over{display:none}.driver-popover-arrow-side-left{left:100%;border-right-color:transparent;border-bottom-color:transparent;border-top-color:transparent}.driver-popover-arrow-side-right{right:100%;border-left-color:transparent;border-bottom-color:transparent;border-top-color:transparent}.driver-popover-arrow-side-top{top:100%;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent}.driver-popover-arrow-side-bottom{bottom:100%;border-left-color:transparent;border-top-color:transparent;border-right-color:transparent}.driver-popover-arrow-side-center{display:none}.driver-popover-arrow-side-left.driver-popover-arrow-align-start,.driver-popover-arrow-side-right.driver-popover-arrow-align-start{top:15px}.driver-popover-arrow-side-top.driver-popover-arrow-align-start,.driver-popover-arrow-side-bottom.driver-popover-arrow-align-start{left:15px}.driver-popover-arrow-align-end.driver-popover-arrow-side-left,.driver-popover-arrow-align-end.driver-popover-arrow-side-right{bottom:15px}.driver-popover-arrow-side-top.driver-popover-arrow-align-end,.driver-popover-arrow-side-bottom.driver-popover-arrow-align-end{right:15px}.driver-popover-arrow-side-left.driver-popover-arrow-align-center,.driver-popover-arrow-side-right.driver-popover-arrow-align-center{top:50%;margin-top:-5px}.driver-popover-arrow-side-top.driver-popover-arrow-align-center,.driver-popover-arrow-side-bottom.driver-popover-arrow-align-center{left:50%;margin-left:-5px}.driver-popover-arrow-none{display:none}
@@ -1,4 +1,5 @@
1
1
  <div class="relative"
2
+ data-mensa-tour="columns"
2
3
  data-controller="mensa-column-customizer"
3
4
  data-mensa-column-customizer-table-name-value="<%= table.name %>"
4
5
  data-mensa-column-customizer-storage-key-value="<%= table.storage_key %>"
@@ -44,7 +44,7 @@
44
44
  </button>
45
45
 
46
46
  <% if table.exportable? %>
47
- <button class="mensa-table__control_bar__button relative" type="button" data-action="mensa-table#export">
47
+ <button class="mensa-table__control_bar__button relative" type="button" data-mensa-tour="export" data-action="mensa-table#export">
48
48
  <i data-mensa-table-target="exportIcon" class="<%= Mensa.config.icons[:control_bar_export] %>"></i>
49
49
  <%= helpers.render partial: "mensa/exports/badge", locals: {table_name: table.name, user: table.current_user, table_params: table.params} %>
50
50
  </button>
@@ -2,6 +2,8 @@
2
2
  <div class="mensa-table__search-bar"
3
3
  id="mensa-filter-pill-list-<%= table.table_id %>"
4
4
  data-controller="mensa-filter-pill-list"
5
+ data-mensa-tour="search"
6
+ data-mensa-tour-filterable="<%= has_filterable_columns %>"
5
7
  data-mensa-table-target="filterList"
6
8
  data-mensa-filter-pill-list-table-name-value="<%= table.name %>"
7
9
  data-mensa-filter-pill-list-storage-key-value="<%= table.storage_key %>"
@@ -313,7 +313,14 @@ export default class FilterPillListComponentController extends ApplicationContro
313
313
  Object.keys(order).length > 0 ||
314
314
  columnOrder.length > 0;
315
315
 
316
- const alreadyRendered = Object.keys(this.renderedFilters()).length > 0;
316
+ // Pills rendered by the server (e.g. a view's default filters) must not win
317
+ // over filters the user changed or cleared.
318
+ const rendered = this.renderedFilters();
319
+ const filtersDiffer =
320
+ filtersProvided &&
321
+ JSON.stringify(rendered) !== JSON.stringify(filters);
322
+ const alreadyRendered =
323
+ Object.keys(rendered).length > 0 && !filtersDiffer;
317
324
 
318
325
  if (!hasState || alreadyRendered || table.frameLoadHandled) {
319
326
  if (typeof table.loadFrame === "function") {
@@ -1,4 +1,5 @@
1
1
  <div class="relative"
2
+ data-mensa-tour="group_by"
2
3
  data-controller="mensa-group-by"
3
4
  data-mensa-group-by-storage-key-value="<%= table.storage_key %>"
4
5
  data-mensa-group-by-group-by-value="<%= table.group_column&.name %>"
@@ -5,10 +5,10 @@
5
5
  data-mensa-table-save-view-url-value="<%= table.path_with_params(helpers.mensa.table_views_path(table.name)) %>"
6
6
  data-mensa-table-views-url-value="<%= table.path_with_params(helpers.mensa.table_views_path(table.name)) %>"
7
7
  data-mensa-table-exports-url-value="<%= table.path_with_params(helpers.mensa.table_exports_path(table.name)) %>"
8
- data-controller="mensa-table"
8
+ data-controller="mensa-table mensa-tour"
9
+ data-mensa-tour-texts-value="<%= I18n.t("mensa.tour").to_json %>"
9
10
  data-mensa-table-storage-key-value="<%= table.storage_key %>"
10
11
  data-action="click@document->mensa-table#saveDropdownOutsideHandler"
11
- data-mensa-table-mensa-filter-pill-outlet="[data-controller='mensa-filter-pill']"
12
12
  data-mensa-table-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
13
13
  data-mensa-table-mensa-column-customizer-outlet="[data-controller='mensa-column-customizer']">
14
14
  <div class="mensa-table__toolbar">
@@ -0,0 +1,182 @@
1
+ import ApplicationController from "mensa/controllers/application_controller";
2
+ import { driver } from "driver.js";
3
+
4
+ // Explains the table's features the first time each one is seen. Every feature is
5
+ // its own segment, remembered in localStorage, so a feature missing on one table is
6
+ // still explained the first time it shows up on another.
7
+ const SEGMENTS = [
8
+ { key: "search", selector: "[data-mensa-tour='search']" },
9
+ { key: "columns", selector: "[data-mensa-tour='columns']" },
10
+ { key: "sorting", selector: "th a.order" },
11
+ { key: "group_by", selector: "[data-mensa-tour='group_by']" },
12
+ { key: "views", selector: "[data-mensa-tour='views']" },
13
+ { key: "export", selector: "[data-mensa-tour='export']" },
14
+ ];
15
+
16
+ const STORAGE_PREFIX = "mensa:tour:seen:";
17
+
18
+ export default class TourController extends ApplicationController {
19
+ static values = { texts: Object };
20
+
21
+ connect() {
22
+ super.connect();
23
+ this._started = false;
24
+ this._hovered = false;
25
+ this._onFrameLoad = () => this._schedule();
26
+ this._onEnter = () => (this._hovered = true);
27
+ this._onLeave = () => (this._hovered = false);
28
+ this._onKeydown = (event) => {
29
+ if (event.key !== "?" || !this._hovered) return;
30
+ if (event.target.closest?.("input, textarea, select, [contenteditable]")) return;
31
+ event.preventDefault();
32
+ this._start(true);
33
+ };
34
+ this.element.addEventListener("turbo:frame-load", this._onFrameLoad);
35
+ this.element.addEventListener("mouseenter", this._onEnter);
36
+ this.element.addEventListener("mouseleave", this._onLeave);
37
+ document.addEventListener("keydown", this._onKeydown);
38
+ }
39
+
40
+ disconnect() {
41
+ this.element.removeEventListener("turbo:frame-load", this._onFrameLoad);
42
+ this.element.removeEventListener("mouseenter", this._onEnter);
43
+ this.element.removeEventListener("mouseleave", this._onLeave);
44
+ document.removeEventListener("keydown", this._onKeydown);
45
+ clearTimeout(this._timer);
46
+ }
47
+
48
+ // Wait for the frame to settle (headers are only rendered after it loads).
49
+ _schedule() {
50
+ if (this._started) return;
51
+ clearTimeout(this._timer);
52
+ this._timer = setTimeout(() => this._start(), 300);
53
+ }
54
+
55
+ // `force` replays every available segment, e.g. when the user presses ?.
56
+ _start(force = false) {
57
+ if ((this._started && !force) || window.__mensaTourActive) return;
58
+
59
+ const segments = SEGMENTS.filter(
60
+ (s) => (force || !this._seen(s.key)) && this.element.querySelector(s.selector),
61
+ );
62
+ if (segments.length === 0) return;
63
+
64
+ this._started = true;
65
+ window.__mensaTourActive = true;
66
+
67
+ const hint = this.textsValue.ui?.replay_hint;
68
+ const steps = segments.map((s, index) => {
69
+ let description = this._description(s.key);
70
+ if (hint && index === segments.length - 1) description += ` ${hint}`;
71
+ const step = {
72
+ element: () => this.element.querySelector(s.selector),
73
+ popover: {
74
+ title: this.textsValue[s.key]?.title,
75
+ description,
76
+ },
77
+ };
78
+ if (s.key === "export") this._makeExportInteractive(step);
79
+ return step;
80
+ });
81
+
82
+ segments.forEach((s) => this._markSeen(s.key));
83
+
84
+ const ui = this.textsValue.ui || {};
85
+ const tour = driver({
86
+ showProgress: true,
87
+ steps,
88
+ nextBtnText: ui.next,
89
+ prevBtnText: ui.previous,
90
+ doneBtnText: ui.done,
91
+ progressText: ui.progress,
92
+ onDestroyed: () => {
93
+ window.__mensaTourActive = false;
94
+ },
95
+ });
96
+ this._activeTour = tour;
97
+ tour.drive();
98
+ }
99
+
100
+ // Next on the export step opens the dialog and continues with the dialog tour.
101
+ _makeExportInteractive(step) {
102
+ step.popover.nextBtnText = this.textsValue.ui?.next;
103
+ step.popover.onNextClick = (el) => {
104
+ this._activeTour?.destroy();
105
+ el?.click();
106
+ setTimeout(() => this._startDialogTour(), 400);
107
+ };
108
+ }
109
+
110
+ _startDialogTour() {
111
+ const dialog = this.element.querySelector("dialog.mensa-table__export-dialog");
112
+ if (!dialog?.open) return;
113
+
114
+ const keys = ["export_list", "export_scope", "export_format", "export_repeat"];
115
+ const steps = keys
116
+ .filter((key) => dialog.querySelector(`[data-mensa-tour='${key}']`))
117
+ .map((key) => ({
118
+ element: () => dialog.querySelector(`[data-mensa-tour='${key}']`),
119
+ popover: {
120
+ title: this.textsValue[key]?.title,
121
+ description: this.textsValue[key]?.description,
122
+ },
123
+ }));
124
+ if (steps.length === 0) return;
125
+
126
+ const ui = this.textsValue.ui || {};
127
+ window.__mensaTourActive = true;
128
+ // The modal dialog lives in the top layer, so the popover has to be moved inside it.
129
+ let wrapper;
130
+ const tour = driver({
131
+ showProgress: true,
132
+ overlayOpacity: 0,
133
+ steps,
134
+ nextBtnText: ui.next,
135
+ prevBtnText: ui.previous,
136
+ doneBtnText: ui.done,
137
+ progressText: ui.progress,
138
+ onPopoverRender: (popover) => {
139
+ wrapper = popover.wrapper;
140
+ dialog.appendChild(wrapper);
141
+ },
142
+ // Driver removes the previous popover from document.body, so put it back first.
143
+ onNextClick: (_el, _step, { driver: d }) => {
144
+ document.body.appendChild(wrapper);
145
+ d.moveNext();
146
+ },
147
+ onPrevClick: (_el, _step, { driver: d }) => {
148
+ document.body.appendChild(wrapper);
149
+ d.movePrevious();
150
+ },
151
+ onDestroyed: () => {
152
+ window.__mensaTourActive = false;
153
+ if (dialog.open) dialog.close();
154
+ },
155
+ });
156
+ tour.drive();
157
+ }
158
+
159
+ _description(key) {
160
+ const text = this.textsValue[key]?.description || "";
161
+ if (key !== "search") return text;
162
+
163
+ const filterable =
164
+ this.element.querySelector("[data-mensa-tour='search']")?.dataset
165
+ .mensaTourFilterable === "true";
166
+ return filterable ? text : this.textsValue.search?.description_only || text;
167
+ }
168
+
169
+ _seen(key) {
170
+ try {
171
+ return window.localStorage.getItem(STORAGE_PREFIX + key) === "1";
172
+ } catch (e) {
173
+ return false;
174
+ }
175
+ }
176
+
177
+ _markSeen(key) {
178
+ try {
179
+ window.localStorage.setItem(STORAGE_PREFIX + key, "1");
180
+ } catch (e) {}
181
+ }
182
+ }
@@ -5,6 +5,7 @@
5
5
  <div class="mensa-table__views"
6
6
  id="mensa-views-<%= table.table_id %>"
7
7
  data-mensa-table-target="views"
8
+ data-mensa-tour="views"
8
9
  data-controller="mensa-views"
9
10
  data-mensa-views-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
10
11
  data-mensa-views-table-id-value="<%= table.table_id %>"
@@ -21,6 +21,9 @@ application.register("mensa-search", SearchComponentController);
21
21
  import TableComponentController from "mensa/components/table/component_controller";
22
22
  application.register("mensa-table", TableComponentController);
23
23
 
24
+ import TourController from "mensa/components/table/tour_controller";
25
+ application.register("mensa-tour", TourController);
26
+
24
27
  import ViewsComponentController from "mensa/components/views/component_controller";
25
28
  application.register("mensa-views", ViewsComponentController);
26
29
 
@@ -167,15 +167,14 @@ module Mensa
167
167
  end
168
168
 
169
169
  # Key under which the browser persists this table's state (filters, order,
170
- # column order, ...). Tables built with different params get separate state.
170
+ # column order, ...), per controller, action and table.
171
171
  def storage_key
172
172
  context = original_view_context if @original_view_context || component
173
- scope = if context.respond_to?(:controller_name) && context.respond_to?(:action_name)
173
+ if context.respond_to?(:controller_name) && context.respond_to?(:action_name)
174
174
  "#{context.controller_name}_#{context.action_name}_#{name}"
175
175
  else
176
- name
176
+ name.to_s
177
177
  end
178
- [scope, Mensa::TableParams.digest(params)].compact.join(":")
179
178
  end
180
179
 
181
180
  def all_views
@@ -10,10 +10,12 @@
10
10
  </header>
11
11
 
12
12
  <div class="mensa-table__export-dialog__body">
13
- <%= render partial: "mensa/exports/list", locals: {table_name: table.name, user: user, table_params: table.params, exports: exports} %>
13
+ <div data-mensa-tour="export_list">
14
+ <%= render partial: "mensa/exports/list", locals: {table_name: table.name, user: user, table_params: table.params, exports: exports} %>
15
+ </div>
14
16
 
15
17
  <form class="mensa-table__export-dialog__form" data-action="submit->mensa-table#confirmExport">
16
- <fieldset class="mensa-table__export-dialog__fieldset">
18
+ <fieldset class="mensa-table__export-dialog__fieldset" data-mensa-tour="export_scope">
17
19
  <legend class="mensa-table__export-dialog__section-title"><%= t("mensa.exports.new_export", default: "New export") %></legend>
18
20
  <label class="mensa-table__export-dialog__option">
19
21
  <input type="radio" name="scope" value="all" checked>
@@ -25,7 +27,7 @@
25
27
  </label>
26
28
  </fieldset>
27
29
 
28
- <fieldset class="mensa-table__export-dialog__fieldset">
30
+ <fieldset class="mensa-table__export-dialog__fieldset" data-mensa-tour="export_format">
29
31
  <legend class="mensa-table__export-dialog__section-title"><%= t("mensa.exports.export_as", default: "Export as") %></legend>
30
32
  <label class="mensa-table__export-dialog__option">
31
33
  <input type="radio" name="export_format" value="csv_excel" checked>
@@ -37,7 +39,7 @@
37
39
  </label>
38
40
  </fieldset>
39
41
 
40
- <fieldset class="mensa-table__export-dialog__fieldset">
42
+ <fieldset class="mensa-table__export-dialog__fieldset" data-mensa-tour="export_repeat">
41
43
  <legend class="mensa-table__export-dialog__section-title"><%= t("mensa.exports.repeat_section", default: "Repeat") %></legend>
42
44
  <label class="mensa-table__export-dialog__option">
43
45
  <input type="radio" name="repeat_mode" value="once" checked data-action="change->mensa-table#toggleExportRepeat">
data/config/importmap.rb CHANGED
@@ -4,4 +4,5 @@ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
4
4
  pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
5
5
  pin_all_from Mensa::Engine.root.join("app/javascript/mensa/controllers"), under: "mensa/controllers", to: "mensa/controllers", preload: false
6
6
  pin_all_from Mensa::Engine.root.join("app/components/mensa"), under: "mensa/components", to: "mensa", preload: false
7
+ pin "driver.js", to: "driver.js.js" # @1.3.6
7
8
  pin "@rails/request.js", to: "@rails--request.js.js" # @0.0.9
@@ -1,5 +1,43 @@
1
1
  en:
2
2
  mensa:
3
+ tour:
4
+ ui:
5
+ next: Next
6
+ previous: Previous
7
+ done: Got it
8
+ replay_hint: Hover over the table and press ? to see this tour again.
9
+ progress: "{{current}} of {{total}}"
10
+ search:
11
+ title: Search and filter
12
+ description: Type to search the table, or click the plus button to filter on a specific column. Active filters appear as pills that you can edit or remove.
13
+ description_only: Type to search the table. Press Enter to apply your search.
14
+ columns:
15
+ title: Columns
16
+ description: Show, hide and reorder the columns by dragging them into the order you prefer.
17
+ sorting:
18
+ title: Sorting
19
+ description: Click a column header to sort by that column. Click again to reverse the order.
20
+ group_by:
21
+ title: Group by
22
+ description: Group rows by a column and optionally show totals or counts per group.
23
+ views:
24
+ title: Views
25
+ description: Switch between saved views, or save your current filters, search and columns as a custom view to reuse later.
26
+ export:
27
+ title: Export
28
+ description: Download the table with your current filters applied. Press Next to open the export dialog.
29
+ export_list:
30
+ title: Your downloads
31
+ description: Finished exports show up here so you can download or delete them.
32
+ export_scope:
33
+ title: What to export
34
+ description: Export all records matching your current filters, or only the page you are looking at.
35
+ export_format:
36
+ title: File format
37
+ description: Choose a CSV optimised for spreadsheet programs, or a plain CSV file.
38
+ export_repeat:
39
+ title: Repeat
40
+ description: Export once, or have the export repeat automatically at a daily, weekly, monthly or other interval.
3
41
  column_customizer:
4
42
  heading: Columns
5
43
  group_by:
@@ -1,5 +1,43 @@
1
1
  nl:
2
2
  mensa:
3
+ tour:
4
+ ui:
5
+ next: Volgende
6
+ previous: Vorige
7
+ done: Begrepen
8
+ replay_hint: Beweeg over de tabel en druk op ? om deze uitleg opnieuw te bekijken.
9
+ progress: "{{current}} van {{total}}"
10
+ search:
11
+ title: Zoeken en filteren
12
+ description: Typ om in de tabel te zoeken, of klik op de plusknop om op een specifieke kolom te filteren. Actieve filters verschijnen als labels die je kunt aanpassen of verwijderen.
13
+ description_only: Typ om in de tabel te zoeken. Druk op Enter om je zoekopdracht toe te passen.
14
+ columns:
15
+ title: Kolommen
16
+ description: Toon, verberg en herorden kolommen door ze te slepen naar de gewenste volgorde.
17
+ sorting:
18
+ title: Sorteren
19
+ description: Klik op een kolomkop om op die kolom te sorteren. Klik nogmaals om de volgorde om te draaien.
20
+ group_by:
21
+ title: Groeperen op
22
+ description: Groepeer rijen op een kolom en toon eventueel totalen of aantallen per groep.
23
+ views:
24
+ title: Weergaven
25
+ description: Wissel tussen opgeslagen weergaven, of bewaar je huidige filters, zoekopdracht en kolommen als eigen weergave om later opnieuw te gebruiken.
26
+ export:
27
+ title: Exporteren
28
+ description: Download de tabel met je huidige filters toegepast. Druk op Volgende om het exportvenster te openen.
29
+ export_list:
30
+ title: Je downloads
31
+ description: Voltooide exports verschijnen hier, zodat je ze kunt downloaden of verwijderen.
32
+ export_scope:
33
+ title: Wat exporteren
34
+ description: Exporteer alle records die overeenkomen met je huidige filters, of alleen de pagina die je nu bekijkt.
35
+ export_format:
36
+ title: Bestandsformaat
37
+ description: Kies een CSV die geoptimaliseerd is voor spreadsheetprogramma's, of een gewoon CSV-bestand.
38
+ export_repeat:
39
+ title: Herhalen
40
+ description: Exporteer eenmalig, of laat de export automatisch herhalen per dag, week, maand of een ander interval.
3
41
  column_customizer:
4
42
  heading: Kolommen
5
43
  group_by:
data/lib/mensa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mensa
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -0,0 +1,666 @@
1
+ let z = {}, J;
2
+ function F(e = {}) {
3
+ z = {
4
+ animate: !0,
5
+ allowClose: !0,
6
+ overlayClickBehavior: "close",
7
+ overlayOpacity: 0.7,
8
+ smoothScroll: !1,
9
+ disableActiveInteraction: !1,
10
+ showProgress: !1,
11
+ stagePadding: 10,
12
+ stageRadius: 5,
13
+ popoverOffset: 10,
14
+ showButtons: ["next", "previous", "close"],
15
+ disableButtons: [],
16
+ overlayColor: "#000",
17
+ ...e
18
+ };
19
+ }
20
+ function s(e) {
21
+ return e ? z[e] : z;
22
+ }
23
+ function le(e) {
24
+ J = e;
25
+ }
26
+ function _() {
27
+ return J;
28
+ }
29
+ let I = {};
30
+ function N(e, o) {
31
+ I[e] = o;
32
+ }
33
+ function L(e) {
34
+ var o;
35
+ (o = I[e]) == null || o.call(I);
36
+ }
37
+ function de() {
38
+ I = {};
39
+ }
40
+ function O(e, o, t, i) {
41
+ return (e /= i / 2) < 1 ? t / 2 * e * e + o : -t / 2 * (--e * (e - 2) - 1) + o;
42
+ }
43
+ function U(e) {
44
+ const o = '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])';
45
+ return e.flatMap((t) => {
46
+ const i = t.matches(o), d = Array.from(t.querySelectorAll(o));
47
+ return [...i ? [t] : [], ...d];
48
+ }).filter((t) => getComputedStyle(t).pointerEvents !== "none" && ve(t));
49
+ }
50
+ function ee(e) {
51
+ if (!e || ue(e))
52
+ return;
53
+ const o = s("smoothScroll"), t = e.offsetHeight > window.innerHeight;
54
+ e.scrollIntoView({
55
+ // Removing the smooth scrolling for elements which exist inside the scrollable parent
56
+ // This was causing the highlight to not properly render
57
+ behavior: !o || pe(e) ? "auto" : "smooth",
58
+ inline: "center",
59
+ block: t ? "start" : "center"
60
+ });
61
+ }
62
+ function pe(e) {
63
+ if (!e || !e.parentElement)
64
+ return;
65
+ const o = e.parentElement;
66
+ return o.scrollHeight > o.clientHeight;
67
+ }
68
+ function ue(e) {
69
+ const o = e.getBoundingClientRect();
70
+ return o.top >= 0 && o.left >= 0 && o.bottom <= (window.innerHeight || document.documentElement.clientHeight) && o.right <= (window.innerWidth || document.documentElement.clientWidth);
71
+ }
72
+ function ve(e) {
73
+ return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
74
+ }
75
+ let D = {};
76
+ function k(e, o) {
77
+ D[e] = o;
78
+ }
79
+ function l(e) {
80
+ return e ? D[e] : D;
81
+ }
82
+ function X() {
83
+ D = {};
84
+ }
85
+ function fe(e, o, t, i) {
86
+ let d = l("__activeStagePosition");
87
+ const n = d || t.getBoundingClientRect(), f = i.getBoundingClientRect(), w = O(e, n.x, f.x - n.x, o), r = O(e, n.y, f.y - n.y, o), v = O(e, n.width, f.width - n.width, o), g = O(e, n.height, f.height - n.height, o);
88
+ d = {
89
+ x: w,
90
+ y: r,
91
+ width: v,
92
+ height: g
93
+ }, oe(d), k("__activeStagePosition", d);
94
+ }
95
+ function te(e) {
96
+ if (!e)
97
+ return;
98
+ const o = e.getBoundingClientRect(), t = {
99
+ x: o.x,
100
+ y: o.y,
101
+ width: o.width,
102
+ height: o.height
103
+ };
104
+ k("__activeStagePosition", t), oe(t);
105
+ }
106
+ function he() {
107
+ const e = l("__activeStagePosition"), o = l("__overlaySvg");
108
+ if (!e)
109
+ return;
110
+ if (!o) {
111
+ console.warn("No stage svg found.");
112
+ return;
113
+ }
114
+ const t = window.innerWidth, i = window.innerHeight;
115
+ o.setAttribute("viewBox", `0 0 ${t} ${i}`);
116
+ }
117
+ function ge(e) {
118
+ const o = we(e);
119
+ document.body.appendChild(o), re(o, (t) => {
120
+ t.target.tagName === "path" && L("overlayClick");
121
+ }), k("__overlaySvg", o);
122
+ }
123
+ function oe(e) {
124
+ const o = l("__overlaySvg");
125
+ if (!o) {
126
+ ge(e);
127
+ return;
128
+ }
129
+ const t = o.firstElementChild;
130
+ if ((t == null ? void 0 : t.tagName) !== "path")
131
+ throw new Error("no path element found in stage svg");
132
+ t.setAttribute("d", ie(e));
133
+ }
134
+ function we(e) {
135
+ const o = window.innerWidth, t = window.innerHeight, i = document.createElementNS("http://www.w3.org/2000/svg", "svg");
136
+ i.classList.add("driver-overlay", "driver-overlay-animated"), i.setAttribute("viewBox", `0 0 ${o} ${t}`), 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%";
137
+ const d = document.createElementNS("http://www.w3.org/2000/svg", "path");
138
+ return d.setAttribute("d", ie(e)), d.style.fill = s("overlayColor") || "rgb(0,0,0)", d.style.opacity = `${s("overlayOpacity")}`, d.style.pointerEvents = "auto", d.style.cursor = "auto", i.appendChild(d), i;
139
+ }
140
+ function ie(e) {
141
+ const o = window.innerWidth, t = window.innerHeight, i = s("stagePadding") || 0, d = s("stageRadius") || 0, n = e.width + i * 2, f = e.height + i * 2, w = Math.min(d, n / 2, f / 2), r = Math.floor(Math.max(w, 0)), v = e.x - i + r, g = e.y - i, y = n - r * 2, a = f - r * 2;
142
+ return `M${o},0L0,0L0,${t}L${o},${t}L${o},0Z
143
+ M${v},${g} h${y} a${r},${r} 0 0 1 ${r},${r} v${a} a${r},${r} 0 0 1 -${r},${r} h-${y} a${r},${r} 0 0 1 -${r},-${r} v-${a} a${r},${r} 0 0 1 ${r},-${r} z`;
144
+ }
145
+ function me() {
146
+ const e = l("__overlaySvg");
147
+ e && e.remove();
148
+ }
149
+ function ye() {
150
+ const e = document.getElementById("driver-dummy-element");
151
+ if (e)
152
+ return e;
153
+ let o = document.createElement("div");
154
+ return o.id = "driver-dummy-element", o.style.width = "0", o.style.height = "0", o.style.pointerEvents = "none", o.style.opacity = "0", o.style.position = "fixed", o.style.top = "50%", o.style.left = "50%", document.body.appendChild(o), o;
155
+ }
156
+ function j(e) {
157
+ const { element: o } = e;
158
+ let t = typeof o == "function" ? o() : typeof o == "string" ? document.querySelector(o) : o;
159
+ t || (t = ye()), be(t, e);
160
+ }
161
+ function xe() {
162
+ const e = l("__activeElement"), o = l("__activeStep");
163
+ e && (te(e), he(), ae(e, o));
164
+ }
165
+ function be(e, o) {
166
+ var C;
167
+ const i = Date.now(), d = l("__activeStep"), n = l("__activeElement") || e, f = !n || n === e, w = e.id === "driver-dummy-element", r = n.id === "driver-dummy-element", v = s("animate"), g = o.onHighlightStarted || s("onHighlightStarted"), y = (o == null ? void 0 : o.onHighlighted) || s("onHighlighted"), a = (d == null ? void 0 : d.onDeselected) || s("onDeselected"), p = s(), c = l();
168
+ !f && a && a(r ? void 0 : n, d, {
169
+ config: p,
170
+ state: c,
171
+ driver: _()
172
+ }), g && g(w ? void 0 : e, o, {
173
+ config: p,
174
+ state: c,
175
+ driver: _()
176
+ });
177
+ const u = !f && v;
178
+ let h = !1;
179
+ _e(), k("previousStep", d), k("previousElement", n), k("activeStep", o), k("activeElement", e);
180
+ const m = () => {
181
+ if (l("__transitionCallback") !== m)
182
+ return;
183
+ const b = Date.now() - i, E = 400 - b <= 400 / 2;
184
+ o.popover && E && !h && u && (Q(e, o), h = !0), s("animate") && b < 400 ? fe(b, 400, n, e) : (te(e), y && y(w ? void 0 : e, o, {
185
+ config: s(),
186
+ state: l(),
187
+ driver: _()
188
+ }), k("__transitionCallback", void 0), k("__previousStep", d), k("__previousElement", n), k("__activeStep", o), k("__activeElement", e)), window.requestAnimationFrame(m);
189
+ };
190
+ k("__transitionCallback", m), window.requestAnimationFrame(m), ee(e), !u && o.popover && Q(e, o), n.classList.remove("driver-active-element", "driver-no-interaction"), n.removeAttribute("aria-haspopup"), n.removeAttribute("aria-expanded"), n.removeAttribute("aria-controls"), ((C = o.disableActiveInteraction) != null ? C : s("disableActiveInteraction")) && e.classList.add("driver-no-interaction"), e.classList.add("driver-active-element"), e.setAttribute("aria-haspopup", "dialog"), e.setAttribute("aria-expanded", "true"), e.setAttribute("aria-controls", "driver-popover-content");
191
+ }
192
+ function Ce() {
193
+ var e;
194
+ (e = document.getElementById("driver-dummy-element")) == null || e.remove(), document.querySelectorAll(".driver-active-element").forEach((o) => {
195
+ o.classList.remove("driver-active-element", "driver-no-interaction"), o.removeAttribute("aria-haspopup"), o.removeAttribute("aria-expanded"), o.removeAttribute("aria-controls");
196
+ });
197
+ }
198
+ function M() {
199
+ const e = l("__resizeTimeout");
200
+ e && window.cancelAnimationFrame(e), k("__resizeTimeout", window.requestAnimationFrame(xe));
201
+ }
202
+ function Pe(e) {
203
+ var r;
204
+ if (!l("isInitialized") || !(e.key === "Tab" || e.keyCode === 9))
205
+ return;
206
+ const i = l("__activeElement"), d = (r = l("popover")) == null ? void 0 : r.wrapper, n = U([
207
+ ...d ? [d] : [],
208
+ ...i ? [i] : []
209
+ ]), f = n[0], w = n[n.length - 1];
210
+ if (e.preventDefault(), e.shiftKey) {
211
+ const v = n[n.indexOf(document.activeElement) - 1] || w;
212
+ v == null || v.focus();
213
+ } else {
214
+ const v = n[n.indexOf(document.activeElement) + 1] || f;
215
+ v == null || v.focus();
216
+ }
217
+ }
218
+ function ne(e) {
219
+ var t;
220
+ ((t = s("allowKeyboardControl")) == null || t) && (e.key === "Escape" ? L("escapePress") : e.key === "ArrowRight" ? L("arrowRightPress") : e.key === "ArrowLeft" && L("arrowLeftPress"));
221
+ }
222
+ function re(e, o, t) {
223
+ const i = (n, f) => {
224
+ const w = n.target;
225
+ e.contains(w) && ((!t || t(w)) && (n.preventDefault(), n.stopPropagation(), n.stopImmediatePropagation()), f == null || f(n));
226
+ };
227
+ document.addEventListener("pointerdown", i, !0), document.addEventListener("mousedown", i, !0), document.addEventListener("pointerup", i, !0), document.addEventListener("mouseup", i, !0), document.addEventListener(
228
+ "click",
229
+ (n) => {
230
+ i(n, o);
231
+ },
232
+ !0
233
+ );
234
+ }
235
+ function ke() {
236
+ window.addEventListener("keyup", ne, !1), window.addEventListener("keydown", Pe, !1), window.addEventListener("resize", M), window.addEventListener("scroll", M);
237
+ }
238
+ function Se() {
239
+ window.removeEventListener("keyup", ne), window.removeEventListener("resize", M), window.removeEventListener("scroll", M);
240
+ }
241
+ function _e() {
242
+ const e = l("popover");
243
+ e && (e.wrapper.style.display = "none");
244
+ }
245
+ function Q(e, o) {
246
+ var b, P;
247
+ let t = l("popover");
248
+ t && document.body.removeChild(t.wrapper), t = Le(), document.body.appendChild(t.wrapper);
249
+ const {
250
+ title: i,
251
+ description: d,
252
+ showButtons: n,
253
+ disableButtons: f,
254
+ showProgress: w,
255
+ nextBtnText: r = s("nextBtnText") || "Next &rarr;",
256
+ prevBtnText: v = s("prevBtnText") || "&larr; Previous",
257
+ progressText: g = s("progressText") || "{current} of {total}"
258
+ } = o.popover || {};
259
+ t.nextButton.innerHTML = r, t.previousButton.innerHTML = v, t.progress.innerHTML = g, i ? (t.title.innerHTML = i, t.title.style.display = "block") : t.title.style.display = "none", d ? (t.description.innerHTML = d, t.description.style.display = "block") : t.description.style.display = "none";
260
+ const y = n || s("showButtons"), a = w || s("showProgress") || !1, p = (y == null ? void 0 : y.includes("next")) || (y == null ? void 0 : y.includes("previous")) || a;
261
+ t.closeButton.style.display = y.includes("close") ? "block" : "none", p ? (t.footer.style.display = "flex", t.progress.style.display = a ? "block" : "none", t.nextButton.style.display = y.includes("next") ? "block" : "none", t.previousButton.style.display = y.includes("previous") ? "block" : "none") : t.footer.style.display = "none";
262
+ const c = f || s("disableButtons") || [];
263
+ c != null && c.includes("next") && (t.nextButton.disabled = !0, t.nextButton.classList.add("driver-popover-btn-disabled")), c != null && c.includes("previous") && (t.previousButton.disabled = !0, t.previousButton.classList.add("driver-popover-btn-disabled")), c != null && c.includes("close") && (t.closeButton.disabled = !0, t.closeButton.classList.add("driver-popover-btn-disabled"));
264
+ const u = t.wrapper;
265
+ u.style.display = "block", u.style.left = "", u.style.top = "", u.style.bottom = "", u.style.right = "", u.id = "driver-popover-content", u.setAttribute("role", "dialog"), u.setAttribute("aria-labelledby", "driver-popover-title"), u.setAttribute("aria-describedby", "driver-popover-description");
266
+ const h = t.arrow;
267
+ h.className = "driver-popover-arrow";
268
+ const m = ((b = o.popover) == null ? void 0 : b.popoverClass) || s("popoverClass") || "";
269
+ u.className = `driver-popover ${m}`.trim(), re(
270
+ t.wrapper,
271
+ (E) => {
272
+ var B, R, W;
273
+ const T = E.target, A = ((B = o.popover) == null ? void 0 : B.onNextClick) || s("onNextClick"), H = ((R = o.popover) == null ? void 0 : R.onPrevClick) || s("onPrevClick"), $ = ((W = o.popover) == null ? void 0 : W.onCloseClick) || s("onCloseClick");
274
+ if (T.closest(".driver-popover-next-btn"))
275
+ return A ? A(e, o, {
276
+ config: s(),
277
+ state: l(),
278
+ driver: _()
279
+ }) : L("nextClick");
280
+ if (T.closest(".driver-popover-prev-btn"))
281
+ return H ? H(e, o, {
282
+ config: s(),
283
+ state: l(),
284
+ driver: _()
285
+ }) : L("prevClick");
286
+ if (T.closest(".driver-popover-close-btn"))
287
+ return $ ? $(e, o, {
288
+ config: s(),
289
+ state: l(),
290
+ driver: _()
291
+ }) : L("closeClick");
292
+ },
293
+ (E) => !(t != null && t.description.contains(E)) && !(t != null && t.title.contains(E)) && typeof E.className == "string" && E.className.includes("driver-popover")
294
+ ), k("popover", t);
295
+ const x = ((P = o.popover) == null ? void 0 : P.onPopoverRender) || s("onPopoverRender");
296
+ x && x(t, {
297
+ config: s(),
298
+ state: l(),
299
+ driver: _()
300
+ }), ae(e, o), ee(u);
301
+ const C = e.classList.contains("driver-dummy-element"), S = U([u, ...C ? [] : [e]]);
302
+ S.length > 0 && S[0].focus();
303
+ }
304
+ function se() {
305
+ const e = l("popover");
306
+ if (!(e != null && e.wrapper))
307
+ return;
308
+ const o = e.wrapper.getBoundingClientRect(), t = s("stagePadding") || 0, i = s("popoverOffset") || 0;
309
+ return {
310
+ width: o.width + t + i,
311
+ height: o.height + t + i,
312
+ realWidth: o.width,
313
+ realHeight: o.height
314
+ };
315
+ }
316
+ function Z(e, o) {
317
+ const { elementDimensions: t, popoverDimensions: i, popoverPadding: d, popoverArrowDimensions: n } = o;
318
+ return e === "start" ? Math.max(
319
+ Math.min(
320
+ t.top - d,
321
+ window.innerHeight - i.realHeight - n.width
322
+ ),
323
+ n.width
324
+ ) : e === "end" ? Math.max(
325
+ Math.min(
326
+ t.top - (i == null ? void 0 : i.realHeight) + t.height + d,
327
+ window.innerHeight - (i == null ? void 0 : i.realHeight) - n.width
328
+ ),
329
+ n.width
330
+ ) : e === "center" ? Math.max(
331
+ Math.min(
332
+ t.top + t.height / 2 - (i == null ? void 0 : i.realHeight) / 2,
333
+ window.innerHeight - (i == null ? void 0 : i.realHeight) - n.width
334
+ ),
335
+ n.width
336
+ ) : 0;
337
+ }
338
+ function G(e, o) {
339
+ const { elementDimensions: t, popoverDimensions: i, popoverPadding: d, popoverArrowDimensions: n } = o;
340
+ return e === "start" ? Math.max(
341
+ Math.min(
342
+ t.left - d,
343
+ window.innerWidth - i.realWidth - n.width
344
+ ),
345
+ n.width
346
+ ) : e === "end" ? Math.max(
347
+ Math.min(
348
+ t.left - (i == null ? void 0 : i.realWidth) + t.width + d,
349
+ window.innerWidth - (i == null ? void 0 : i.realWidth) - n.width
350
+ ),
351
+ n.width
352
+ ) : e === "center" ? Math.max(
353
+ Math.min(
354
+ t.left + t.width / 2 - (i == null ? void 0 : i.realWidth) / 2,
355
+ window.innerWidth - (i == null ? void 0 : i.realWidth) - n.width
356
+ ),
357
+ n.width
358
+ ) : 0;
359
+ }
360
+ function ae(e, o) {
361
+ const t = l("popover");
362
+ if (!t)
363
+ return;
364
+ const { align: i = "start", side: d = "left" } = (o == null ? void 0 : o.popover) || {}, n = i, f = e.id === "driver-dummy-element" ? "over" : d, w = s("stagePadding") || 0, r = se(), v = t.arrow.getBoundingClientRect(), g = e.getBoundingClientRect(), y = g.top - r.height;
365
+ let a = y >= 0;
366
+ const p = window.innerHeight - (g.bottom + r.height);
367
+ let c = p >= 0;
368
+ const u = g.left - r.width;
369
+ let h = u >= 0;
370
+ const m = window.innerWidth - (g.right + r.width);
371
+ let x = m >= 0;
372
+ const C = !a && !c && !h && !x;
373
+ let S = f;
374
+ if (f === "top" && a ? x = h = c = !1 : f === "bottom" && c ? x = h = a = !1 : f === "left" && h ? x = a = c = !1 : f === "right" && x && (h = a = c = !1), f === "over") {
375
+ const b = window.innerWidth / 2 - r.realWidth / 2, P = window.innerHeight / 2 - r.realHeight / 2;
376
+ t.wrapper.style.left = `${b}px`, t.wrapper.style.right = "auto", t.wrapper.style.top = `${P}px`, t.wrapper.style.bottom = "auto";
377
+ } else if (C) {
378
+ const b = window.innerWidth / 2 - (r == null ? void 0 : r.realWidth) / 2, P = 10;
379
+ t.wrapper.style.left = `${b}px`, t.wrapper.style.right = "auto", t.wrapper.style.bottom = `${P}px`, t.wrapper.style.top = "auto";
380
+ } else if (h) {
381
+ const b = Math.min(
382
+ u,
383
+ window.innerWidth - (r == null ? void 0 : r.realWidth) - v.width
384
+ ), P = Z(n, {
385
+ elementDimensions: g,
386
+ popoverDimensions: r,
387
+ popoverPadding: w,
388
+ popoverArrowDimensions: v
389
+ });
390
+ t.wrapper.style.left = `${b}px`, t.wrapper.style.top = `${P}px`, t.wrapper.style.bottom = "auto", t.wrapper.style.right = "auto", S = "left";
391
+ } else if (x) {
392
+ const b = Math.min(
393
+ m,
394
+ window.innerWidth - (r == null ? void 0 : r.realWidth) - v.width
395
+ ), P = Z(n, {
396
+ elementDimensions: g,
397
+ popoverDimensions: r,
398
+ popoverPadding: w,
399
+ popoverArrowDimensions: v
400
+ });
401
+ t.wrapper.style.right = `${b}px`, t.wrapper.style.top = `${P}px`, t.wrapper.style.bottom = "auto", t.wrapper.style.left = "auto", S = "right";
402
+ } else if (a) {
403
+ const b = Math.min(
404
+ y,
405
+ window.innerHeight - r.realHeight - v.width
406
+ );
407
+ let P = G(n, {
408
+ elementDimensions: g,
409
+ popoverDimensions: r,
410
+ popoverPadding: w,
411
+ popoverArrowDimensions: v
412
+ });
413
+ t.wrapper.style.top = `${b}px`, t.wrapper.style.left = `${P}px`, t.wrapper.style.bottom = "auto", t.wrapper.style.right = "auto", S = "top";
414
+ } else if (c) {
415
+ const b = Math.min(
416
+ p,
417
+ window.innerHeight - (r == null ? void 0 : r.realHeight) - v.width
418
+ );
419
+ let P = G(n, {
420
+ elementDimensions: g,
421
+ popoverDimensions: r,
422
+ popoverPadding: w,
423
+ popoverArrowDimensions: v
424
+ });
425
+ t.wrapper.style.left = `${P}px`, t.wrapper.style.bottom = `${b}px`, t.wrapper.style.top = "auto", t.wrapper.style.right = "auto", S = "bottom";
426
+ }
427
+ C ? t.arrow.classList.add("driver-popover-arrow-none") : Ee(n, S, e);
428
+ }
429
+ function Ee(e, o, t) {
430
+ const i = l("popover");
431
+ if (!i)
432
+ return;
433
+ const d = t.getBoundingClientRect(), n = se(), f = i.arrow, w = n.width, r = window.innerWidth, v = d.width, g = d.left, y = n.height, a = window.innerHeight, p = d.top, c = d.height;
434
+ f.className = "driver-popover-arrow";
435
+ let u = o, h = e;
436
+ if (o === "top" ? (g + v <= 0 ? (u = "right", h = "end") : g + v - w <= 0 && (u = "top", h = "start"), g >= r ? (u = "left", h = "end") : g + w >= r && (u = "top", h = "end")) : o === "bottom" ? (g + v <= 0 ? (u = "right", h = "start") : g + v - w <= 0 && (u = "bottom", h = "start"), g >= r ? (u = "left", h = "start") : g + w >= r && (u = "bottom", h = "end")) : o === "left" ? (p + c <= 0 ? (u = "bottom", h = "end") : p + c - y <= 0 && (u = "left", h = "start"), p >= a ? (u = "top", h = "end") : p + y >= a && (u = "left", h = "end")) : o === "right" && (p + c <= 0 ? (u = "bottom", h = "start") : p + c - y <= 0 && (u = "right", h = "start"), p >= a ? (u = "top", h = "start") : p + y >= a && (u = "right", h = "end")), !u)
437
+ f.classList.add("driver-popover-arrow-none");
438
+ else {
439
+ f.classList.add(`driver-popover-arrow-side-${u}`), f.classList.add(`driver-popover-arrow-align-${h}`);
440
+ const m = t.getBoundingClientRect(), x = f.getBoundingClientRect(), C = s("stagePadding") || 0, S = m.left - C < window.innerWidth && m.right + C > 0 && m.top - C < window.innerHeight && m.bottom + C > 0;
441
+ o === "bottom" && S && (x.x > m.x && x.x + x.width < m.x + m.width ? i.wrapper.style.transform = "translateY(0)" : (f.classList.remove(`driver-popover-arrow-align-${h}`), f.classList.add("driver-popover-arrow-none"), i.wrapper.style.transform = `translateY(-${C / 2}px)`));
442
+ }
443
+ }
444
+ function Le() {
445
+ const e = document.createElement("div");
446
+ e.classList.add("driver-popover");
447
+ const o = document.createElement("div");
448
+ o.classList.add("driver-popover-arrow");
449
+ const t = document.createElement("header");
450
+ t.id = "driver-popover-title", t.classList.add("driver-popover-title"), t.style.display = "none", t.innerText = "Popover Title";
451
+ const i = document.createElement("div");
452
+ i.id = "driver-popover-description", i.classList.add("driver-popover-description"), i.style.display = "none", i.innerText = "Popover description is here";
453
+ const d = document.createElement("button");
454
+ d.type = "button", d.classList.add("driver-popover-close-btn"), d.setAttribute("aria-label", "Close"), d.innerHTML = "&times;";
455
+ const n = document.createElement("footer");
456
+ n.classList.add("driver-popover-footer");
457
+ const f = document.createElement("span");
458
+ f.classList.add("driver-popover-progress-text"), f.innerText = "";
459
+ const w = document.createElement("span");
460
+ w.classList.add("driver-popover-navigation-btns");
461
+ const r = document.createElement("button");
462
+ r.type = "button", r.classList.add("driver-popover-prev-btn"), r.innerHTML = "&larr; Previous";
463
+ const v = document.createElement("button");
464
+ return v.type = "button", v.classList.add("driver-popover-next-btn"), v.innerHTML = "Next &rarr;", w.appendChild(r), w.appendChild(v), n.appendChild(f), n.appendChild(w), e.appendChild(d), e.appendChild(o), e.appendChild(t), e.appendChild(i), e.appendChild(n), {
465
+ wrapper: e,
466
+ arrow: o,
467
+ title: t,
468
+ description: i,
469
+ footer: n,
470
+ previousButton: r,
471
+ nextButton: v,
472
+ closeButton: d,
473
+ footerButtons: w,
474
+ progress: f
475
+ };
476
+ }
477
+ function Te() {
478
+ var o;
479
+ const e = l("popover");
480
+ e && ((o = e.wrapper.parentElement) == null || o.removeChild(e.wrapper));
481
+ }
482
+ function Ae(e = {}) {
483
+ F(e);
484
+ function o() {
485
+ s("allowClose") && g();
486
+ }
487
+ function t() {
488
+ const a = s("overlayClickBehavior");
489
+ if (s("allowClose") && a === "close") {
490
+ g();
491
+ return;
492
+ }
493
+ a === "nextStep" && i();
494
+ }
495
+ function i() {
496
+ const a = l("activeIndex"), p = s("steps") || [];
497
+ if (typeof a == "undefined")
498
+ return;
499
+ const c = a + 1;
500
+ p[c] ? v(c) : g();
501
+ }
502
+ function d() {
503
+ const a = l("activeIndex"), p = s("steps") || [];
504
+ if (typeof a == "undefined")
505
+ return;
506
+ const c = a - 1;
507
+ p[c] ? v(c) : g();
508
+ }
509
+ function n(a) {
510
+ (s("steps") || [])[a] ? v(a) : g();
511
+ }
512
+ function f() {
513
+ var x;
514
+ if (l("__transitionCallback"))
515
+ return;
516
+ const p = l("activeIndex"), c = l("__activeStep"), u = l("__activeElement");
517
+ if (typeof p == "undefined" || typeof c == "undefined" || typeof l("activeIndex") == "undefined")
518
+ return;
519
+ const m = ((x = c.popover) == null ? void 0 : x.onPrevClick) || s("onPrevClick");
520
+ if (m)
521
+ return m(u, c, {
522
+ config: s(),
523
+ state: l(),
524
+ driver: _()
525
+ });
526
+ d();
527
+ }
528
+ function w() {
529
+ var m;
530
+ if (l("__transitionCallback"))
531
+ return;
532
+ const p = l("activeIndex"), c = l("__activeStep"), u = l("__activeElement");
533
+ if (typeof p == "undefined" || typeof c == "undefined")
534
+ return;
535
+ const h = ((m = c.popover) == null ? void 0 : m.onNextClick) || s("onNextClick");
536
+ if (h)
537
+ return h(u, c, {
538
+ config: s(),
539
+ state: l(),
540
+ driver: _()
541
+ });
542
+ i();
543
+ }
544
+ function r() {
545
+ l("isInitialized") || (k("isInitialized", !0), document.body.classList.add("driver-active", s("animate") ? "driver-fade" : "driver-simple"), ke(), N("overlayClick", t), N("escapePress", o), N("arrowLeftPress", f), N("arrowRightPress", w));
546
+ }
547
+ function v(a = 0) {
548
+ var $, B, R, W, V, q, K, Y;
549
+ const p = s("steps");
550
+ if (!p) {
551
+ console.error("No steps to drive through"), g();
552
+ return;
553
+ }
554
+ if (!p[a]) {
555
+ g();
556
+ return;
557
+ }
558
+ k("__activeOnDestroyed", document.activeElement), k("activeIndex", a);
559
+ const c = p[a], u = p[a + 1], h = p[a - 1], m = (($ = c.popover) == null ? void 0 : $.doneBtnText) || s("doneBtnText") || "Done", x = s("allowClose"), C = typeof ((B = c.popover) == null ? void 0 : B.showProgress) != "undefined" ? (R = c.popover) == null ? void 0 : R.showProgress : s("showProgress"), b = (((W = c.popover) == null ? void 0 : W.progressText) || s("progressText") || "{{current}} of {{total}}").replace("{{current}}", `${a + 1}`).replace("{{total}}", `${p.length}`), P = ((V = c.popover) == null ? void 0 : V.showButtons) || s("showButtons"), E = [
560
+ "next",
561
+ "previous",
562
+ ...x ? ["close"] : []
563
+ ].filter((ce) => !(P != null && P.length) || P.includes(ce)), T = ((q = c.popover) == null ? void 0 : q.onNextClick) || s("onNextClick"), A = ((K = c.popover) == null ? void 0 : K.onPrevClick) || s("onPrevClick"), H = ((Y = c.popover) == null ? void 0 : Y.onCloseClick) || s("onCloseClick");
564
+ j({
565
+ ...c,
566
+ popover: {
567
+ showButtons: E,
568
+ nextBtnText: u ? void 0 : m,
569
+ disableButtons: [...h ? [] : ["previous"]],
570
+ showProgress: C,
571
+ progressText: b,
572
+ onNextClick: T || (() => {
573
+ u ? v(a + 1) : g();
574
+ }),
575
+ onPrevClick: A || (() => {
576
+ v(a - 1);
577
+ }),
578
+ onCloseClick: H || (() => {
579
+ g();
580
+ }),
581
+ ...(c == null ? void 0 : c.popover) || {}
582
+ }
583
+ });
584
+ }
585
+ function g(a = !0) {
586
+ const p = l("__activeElement"), c = l("__activeStep"), u = l("__activeOnDestroyed"), h = s("onDestroyStarted");
587
+ if (a && h) {
588
+ const C = !p || (p == null ? void 0 : p.id) === "driver-dummy-element";
589
+ h(C ? void 0 : p, c, {
590
+ config: s(),
591
+ state: l(),
592
+ driver: _()
593
+ });
594
+ return;
595
+ }
596
+ const m = (c == null ? void 0 : c.onDeselected) || s("onDeselected"), x = s("onDestroyed");
597
+ if (document.body.classList.remove("driver-active", "driver-fade", "driver-simple"), Se(), Te(), Ce(), me(), de(), X(), p && c) {
598
+ const C = p.id === "driver-dummy-element";
599
+ m && m(C ? void 0 : p, c, {
600
+ config: s(),
601
+ state: l(),
602
+ driver: _()
603
+ }), x && x(C ? void 0 : p, c, {
604
+ config: s(),
605
+ state: l(),
606
+ driver: _()
607
+ });
608
+ }
609
+ u && u.focus();
610
+ }
611
+ const y = {
612
+ isActive: () => l("isInitialized") || !1,
613
+ refresh: M,
614
+ drive: (a = 0) => {
615
+ r(), v(a);
616
+ },
617
+ setConfig: F,
618
+ setSteps: (a) => {
619
+ X(), F({
620
+ ...s(),
621
+ steps: a
622
+ });
623
+ },
624
+ getConfig: s,
625
+ getState: l,
626
+ getActiveIndex: () => l("activeIndex"),
627
+ isFirstStep: () => l("activeIndex") === 0,
628
+ isLastStep: () => {
629
+ const a = s("steps") || [], p = l("activeIndex");
630
+ return p !== void 0 && p === a.length - 1;
631
+ },
632
+ getActiveStep: () => l("activeStep"),
633
+ getActiveElement: () => l("activeElement"),
634
+ getPreviousElement: () => l("previousElement"),
635
+ getPreviousStep: () => l("previousStep"),
636
+ moveNext: i,
637
+ movePrevious: d,
638
+ moveTo: n,
639
+ hasNextStep: () => {
640
+ const a = s("steps") || [], p = l("activeIndex");
641
+ return p !== void 0 && !!a[p + 1];
642
+ },
643
+ hasPreviousStep: () => {
644
+ const a = s("steps") || [], p = l("activeIndex");
645
+ return p !== void 0 && !!a[p - 1];
646
+ },
647
+ highlight: (a) => {
648
+ r(), j({
649
+ ...a,
650
+ popover: a.popover ? {
651
+ showButtons: [],
652
+ showProgress: !1,
653
+ progressText: "",
654
+ ...a.popover
655
+ } : void 0
656
+ });
657
+ },
658
+ destroy: () => {
659
+ g(!1);
660
+ }
661
+ };
662
+ return le(y), y;
663
+ }
664
+ export {
665
+ Ae as driver
666
+ };
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mensa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
@@ -188,6 +188,7 @@ files:
188
188
  - app/assets/config/mensa_manifest.js
189
189
  - app/assets/images/mensa/.keep
190
190
  - app/assets/stylesheets/mensa/application.css
191
+ - app/assets/stylesheets/mensa/driver.css
191
192
  - app/assets/tailwind/mensa/engine.css
192
193
  - app/components/mensa/add_filter/component.css
193
194
  - app/components/mensa/add_filter/component.html.erb
@@ -239,6 +240,7 @@ files:
239
240
  - app/components/mensa/table/component.html.erb
240
241
  - app/components/mensa/table/component.rb
241
242
  - app/components/mensa/table/component_controller.js
243
+ - app/components/mensa/table/tour_controller.js
242
244
  - app/components/mensa/table_row/component.css
243
245
  - app/components/mensa/table_row/component.html.erb
244
246
  - app/components/mensa/table_row/component.rb
@@ -343,6 +345,7 @@ files:
343
345
  - package-lock.json
344
346
  - package.json
345
347
  - vendor/javascript/@rails--request.js.js
348
+ - vendor/javascript/driver.js.js
346
349
  homepage: https://github.com/entdec/mensa
347
350
  licenses:
348
351
  - MIT