activeadmin_configurable_columns 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +76 -11
- data/app/javascript/activeadmin_configurable_columns/picker.js +56 -0
- data/app/models/activeadmin_configurable_columns/preference.rb +29 -8
- data/app/views/activeadmin_configurable_columns/_form.html.erb +27 -12
- data/app/views/activeadmin_configurable_columns/_script.html.erb +6 -0
- data/config/importmap.rb +4 -0
- data/config/locales/en.yml +4 -0
- data/db/migrate/20261026000001_add_column_order_to_activeadmin_configurable_columns_preferences.rb +7 -0
- data/lib/activeadmin_configurable_columns/admin/table_columns.rb +14 -8
- data/lib/activeadmin_configurable_columns/engine.rb +19 -0
- data/lib/activeadmin_configurable_columns/registry.rb +16 -0
- data/lib/activeadmin_configurable_columns/saved_selection.rb +8 -0
- data/lib/activeadmin_configurable_columns/selection.rb +40 -0
- data/lib/activeadmin_configurable_columns/table_renderer.rb +3 -2
- data/lib/activeadmin_configurable_columns/version.rb +1 -1
- data/lib/activeadmin_configurable_columns.rb +24 -6
- data/vendor/javascript/activeadmin_configurable_columns/sortable.js +23 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63e61576e8abe89e94e69f4ca00f8ce7392f07c5faed46ba4fbe2653c27ca168
|
|
4
|
+
data.tar.gz: 1317c5848157b220c8e5d26ad89dc044513a892a3eb0ab27dfe1b13337713ea8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ed54d53cacd26e8b721534c7f9846cc0a631cb4a5c4bde2e11e13b507e992e2e8c7541f33ef3231c4f020dfc99cec2c2567333a64b8860a9dc704e71962f4d8
|
|
7
|
+
data.tar.gz: 3478c1812115a1975416e0d4ba6f6fe513ea3a7679a1411205188328c310366fe684fab699abff43786f6136bcabc721becb1f1df9679c6c7ba3870bbd5c18b6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.0 (2026-10-26)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Drag-and-drop column reordering in the "Columns" picker and on the "Table
|
|
8
|
+
columns" page, with arrow-key reordering on the focused handle and touch
|
|
9
|
+
support. The index table renders the visible columns in the admin's order.
|
|
10
|
+
- "Reset to defaults" button: forgets the admin's choice for a table.
|
|
11
|
+
- Columns added to a declaration after an admin saved an order appear next to
|
|
12
|
+
their declared neighbour, and are visible when declared `default: true`.
|
|
13
|
+
|
|
14
|
+
### Changed (breaking)
|
|
15
|
+
|
|
16
|
+
- New migration adding a nullable `column_order` column to the preferences
|
|
17
|
+
table. Rows saved before it keep declaration order until saved again.
|
|
18
|
+
- Preference store contract: `visible_column_keys(admin, resource_key)` became
|
|
19
|
+
`fetch(admin, resource_key)`, returning a `SavedSelection` (or nil);
|
|
20
|
+
`store` takes `visible_keys:` and `order_keys:` instead of `keys:`; a new
|
|
21
|
+
`clear(admin:, resource_key:)` is required.
|
|
22
|
+
- `ActiveadminConfigurableColumns.visible_columns` is replaced by
|
|
23
|
+
`selection_for(registry, admin)`.
|
|
24
|
+
- The picker form no longer sends an empty `visible_columns[]` placeholder; a
|
|
25
|
+
missing parameter falls back to the defaults as before.
|
|
26
|
+
|
|
3
27
|
## 1.0.0 (2026-09-30)
|
|
4
28
|
|
|
5
29
|
Initial release.
|
data/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
# activeadmin_configurable_columns
|
|
1
|
+
# activeadmin_configurable_columns [](https://github.com/kyryloliinyk/activeadmin_configurable_columns/actions/workflows/ci.yml)
|
|
2
|
+
|
|
2
3
|
|
|
3
|
-
[](https://github.com/kyryloliinyk/activeadmin_configurable_columns/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
5
|
Per-admin configurable index table columns for [ActiveAdmin](https://activeadmin.info) 4.
|
|
6
6
|
|
|
7
7
|
ActiveAdmin has no built-in way to let each admin choose which columns of an
|
|
8
|
-
index table they see. This gem adds one, built on
|
|
9
|
-
points:
|
|
8
|
+
index table they see, or in which order. This gem adds one, built on
|
|
9
|
+
ActiveAdmin's own extension points:
|
|
10
10
|
|
|
11
11
|
- a `configurable_columns do ... end` DSL declares the columns an admin may pick from;
|
|
12
|
-
- `configurable_columns` inside an `index` block renders the ones the current admin chose;
|
|
13
|
-
- a "Columns" picker appears in the sidebar of every configurable index
|
|
12
|
+
- `configurable_columns` inside an `index` block renders the ones the current admin chose, in their order;
|
|
13
|
+
- a "Columns" picker appears in the sidebar of every configurable index, where columns are
|
|
14
|
+
ticked to show them and dragged to reorder them;
|
|
14
15
|
- a "Table columns" admin page lets an admin review and change every table at once;
|
|
15
|
-
- each admin's choice is stored in the database, per admin and per table.
|
|
16
|
+
- each admin's choice and order is stored in the database, per admin and per table.
|
|
16
17
|
|
|
17
18
|
## Requirements
|
|
18
19
|
|
|
@@ -25,7 +26,7 @@ points:
|
|
|
25
26
|
Add the gem to your Gemfile:
|
|
26
27
|
|
|
27
28
|
```ruby
|
|
28
|
-
gem 'activeadmin_configurable_columns'
|
|
29
|
+
gem 'activeadmin_configurable_columns', '~> 2.0'
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
Install the migration and migrate:
|
|
@@ -79,6 +80,34 @@ saved per admin; unknown or stale keys are dropped on read and write, and an
|
|
|
79
80
|
empty selection falls back to the declared defaults, so an admin can never end
|
|
80
81
|
up with an empty table.
|
|
81
82
|
|
|
83
|
+
## Reordering columns
|
|
84
|
+
|
|
85
|
+
Every row of the picker has a drag handle. Dragging rows and pressing "Save
|
|
86
|
+
columns" stores the new order; the table renders the visible columns in that
|
|
87
|
+
order. `selectable_column` and `actions` stay outside the declaration, so they
|
|
88
|
+
keep their places at the edges of the table.
|
|
89
|
+
|
|
90
|
+
- **Keyboard:** focus a handle (Tab) and press the up or down arrow to move its row.
|
|
91
|
+
- **Touch:** press and hold a handle briefly, then drag; a short swipe scrolls the page.
|
|
92
|
+
- **Reset to defaults** forgets the admin's choice for that table: the declared
|
|
93
|
+
defaults, in declaration order.
|
|
94
|
+
- **New columns:** a column added to a declaration later appears next to the
|
|
95
|
+
column it follows in the declaration, and is visible if it is `default: true`.
|
|
96
|
+
Selections saved with gem versions before reordering existed keep showing
|
|
97
|
+
exactly what was ticked, in declaration order, until they are saved again.
|
|
98
|
+
- **Without JavaScript** the picker still saves visibility; it just cannot reorder.
|
|
99
|
+
|
|
100
|
+
The picker script is an ES module served by the gem and pinned into
|
|
101
|
+
ActiveAdmin's importmap, so an ActiveAdmin 4 installation using
|
|
102
|
+
importmap-rails (the default) needs no extra setup. It uses
|
|
103
|
+
[SortableJS](https://github.com/SortableJS/Sortable) (MIT), vendored in
|
|
104
|
+
`vendor/javascript/activeadmin_configurable_columns/sortable.js`.
|
|
105
|
+
|
|
106
|
+
If your host builds ActiveAdmin's JavaScript with jsbundling instead, add the
|
|
107
|
+
gem's `app/javascript` directory to your bundler's resolve paths, install
|
|
108
|
+
`sortablejs`, and import `activeadmin_configurable_columns/picker` from your
|
|
109
|
+
ActiveAdmin entrypoint.
|
|
110
|
+
|
|
82
111
|
## Configuration
|
|
83
112
|
|
|
84
113
|
All settings are optional. In `config/initializers/activeadmin_configurable_columns.rb`:
|
|
@@ -94,13 +123,19 @@ ActiveadminConfigurableColumns.configure do |config|
|
|
|
94
123
|
config.sidebar_priority = 20
|
|
95
124
|
|
|
96
125
|
# Where selections are read from and written to. Any object answering
|
|
97
|
-
#
|
|
98
|
-
# store(admin:, resource_key:,
|
|
126
|
+
# fetch(admin, resource_key) -> SavedSelection | nil (nil = never saved)
|
|
127
|
+
# store(admin:, resource_key:, visible_keys:, order_keys:)
|
|
128
|
+
# clear(admin:, resource_key:)
|
|
99
129
|
# can be plugged in. Default: the bundled Preference model.
|
|
100
130
|
config.preference_store = MyOwnStore
|
|
101
131
|
end
|
|
102
132
|
```
|
|
103
133
|
|
|
134
|
+
`fetch` returns an `ActiveadminConfigurableColumns::SavedSelection`, whose
|
|
135
|
+
`visible_keys` are the ticked keys and whose `order_keys` are every key in the
|
|
136
|
+
admin's order, or `nil` for a selection saved without one (declaration order).
|
|
137
|
+
The gem normalizes keys against the declaration before calling `store`.
|
|
138
|
+
|
|
104
139
|
The current admin is whatever ActiveAdmin's `current_active_admin_user`
|
|
105
140
|
returns, so the gem follows your `config.current_user_method` setting.
|
|
106
141
|
|
|
@@ -120,6 +155,10 @@ en:
|
|
|
120
155
|
saved: "Columns saved"
|
|
121
156
|
empty: "No table has configurable columns yet."
|
|
122
157
|
unknown_resource: "Unknown table"
|
|
158
|
+
reset: "Reset to defaults"
|
|
159
|
+
reset_done: "Columns reset to defaults"
|
|
160
|
+
move: "Move %{column}"
|
|
161
|
+
reorder_hint: "Drag to reorder, or focus and use the arrow keys"
|
|
123
162
|
```
|
|
124
163
|
|
|
125
164
|
Column checkbox labels come from `human_attribute_name`, so your existing
|
|
@@ -138,6 +177,28 @@ config) so the picker and the "Table columns" page keep their layout:
|
|
|
138
177
|
If a rake task generates your ActiveAdmin Tailwind entrypoint, resolve the path
|
|
139
178
|
with `Gem.loaded_specs['activeadmin_configurable_columns'].full_gem_path`.
|
|
140
179
|
|
|
180
|
+
## Upgrading from 1.x
|
|
181
|
+
|
|
182
|
+
Version 2.0 adds column reordering and changes the preference store contract.
|
|
183
|
+
|
|
184
|
+
1. Update the Gemfile requirement to `'~> 2.0'` and run `bundle update activeadmin_configurable_columns`.
|
|
185
|
+
2. Install and run the new migration, which adds the nullable `column_order`
|
|
186
|
+
column. Existing rows keep working unchanged:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
bin/rails activeadmin_configurable_columns:install:migrations
|
|
190
|
+
bin/rails db:migrate
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
3. Rebuild your ActiveAdmin Tailwind stylesheet so the picker's new classes are
|
|
194
|
+
emitted.
|
|
195
|
+
4. Add the four new translation keys above to every locale you override.
|
|
196
|
+
5. A custom `preference_store` must implement the new contract:
|
|
197
|
+
`visible_column_keys` became `fetch`, `store` takes `visible_keys:` and
|
|
198
|
+
`order_keys:` instead of `keys:`, and `clear` is new.
|
|
199
|
+
`ActiveadminConfigurableColumns.visible_columns` is replaced by
|
|
200
|
+
`ActiveadminConfigurableColumns.selection_for(registry, admin)`.
|
|
201
|
+
|
|
141
202
|
## How it works
|
|
142
203
|
|
|
143
204
|
- `ActiveadminConfigurableColumns::Dsl` is mixed into `ActiveAdmin::ResourceDSL`;
|
|
@@ -145,7 +206,11 @@ with `Gem.loaded_specs['activeadmin_configurable_columns'].full_gem_path`.
|
|
|
145
206
|
the resource, and adds the sidebar section.
|
|
146
207
|
- `ActiveadminConfigurableColumns::TableRenderer` is mixed into
|
|
147
208
|
`ActiveAdmin::Views::IndexAsTable::IndexTableFor`; `configurable_columns` inside
|
|
148
|
-
`index`
|
|
209
|
+
`index` resolves the current admin's `Selection` (order and visibility) and
|
|
210
|
+
emits the visible columns in order.
|
|
211
|
+
- The picker form gives every row a hidden `columns[]` input. Browsers submit
|
|
212
|
+
fields in document order, so dragging rows is all the script does; the server
|
|
213
|
+
receives the full order and the ticked subset with no serialization step.
|
|
149
214
|
- The "Table columns" page is a regular `ActiveAdmin.register_page`, loaded
|
|
150
215
|
through ActiveAdmin's own load paths; its update action also serves the
|
|
151
216
|
sidebar forms.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Drag-and-drop reordering for the "Columns" picker.
|
|
2
|
+
//
|
|
3
|
+
// Every row carries a hidden `columns[]` input, and browsers submit fields in
|
|
4
|
+
// document order, so moving a row is all it takes: the form posts the new
|
|
5
|
+
// order on save. Without this script the form still saves visibility.
|
|
6
|
+
import Sortable from "activeadmin_configurable_columns/sortable"
|
|
7
|
+
|
|
8
|
+
const LIST = "[data-aacc-list]"
|
|
9
|
+
const ROW = "[data-aacc-row]"
|
|
10
|
+
const HANDLE = "[data-aacc-handle]"
|
|
11
|
+
|
|
12
|
+
// Keyboard alternative to dragging: arrow keys on a focused handle.
|
|
13
|
+
const moveWithKeyboard = (event) => {
|
|
14
|
+
const handle = event.target.closest(HANDLE)
|
|
15
|
+
if (!handle || (event.key !== "ArrowUp" && event.key !== "ArrowDown")) return
|
|
16
|
+
|
|
17
|
+
event.preventDefault()
|
|
18
|
+
const row = handle.closest(ROW)
|
|
19
|
+
|
|
20
|
+
if (event.key === "ArrowUp" && row.previousElementSibling) {
|
|
21
|
+
row.previousElementSibling.before(row)
|
|
22
|
+
} else if (event.key === "ArrowDown" && row.nextElementSibling) {
|
|
23
|
+
row.nextElementSibling.after(row)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Moving a node can drop its focus; keep the keyboard user where they were.
|
|
27
|
+
handle.focus()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const setUp = (list) => {
|
|
31
|
+
if ("aaccReady" in list.dataset) return
|
|
32
|
+
list.dataset.aaccReady = ""
|
|
33
|
+
|
|
34
|
+
Sortable.create(list, {
|
|
35
|
+
handle: HANDLE,
|
|
36
|
+
draggable: ROW,
|
|
37
|
+
animation: 150,
|
|
38
|
+
// On touch screens a short press scrolls the page; a long press drags.
|
|
39
|
+
delay: 150,
|
|
40
|
+
delayOnTouchOnly: true,
|
|
41
|
+
// Single class names only (Sortable adds them with classList.add); the
|
|
42
|
+
// list styles them with Tailwind arbitrary variants.
|
|
43
|
+
ghostClass: "aacc-ghost",
|
|
44
|
+
chosenClass: "aacc-chosen",
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
list.addEventListener("keydown", moveWithKeyboard)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const start = () => document.querySelectorAll(LIST).forEach(setUp)
|
|
51
|
+
|
|
52
|
+
if (document.readyState === "loading") {
|
|
53
|
+
document.addEventListener("DOMContentLoaded", start)
|
|
54
|
+
} else {
|
|
55
|
+
start()
|
|
56
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module ActiveadminConfigurableColumns
|
|
2
2
|
# One row per admin per ActiveAdmin index table, holding the columns that
|
|
3
|
-
# admin chose to see there
|
|
4
|
-
# the ActiveAdmin registration with the
|
|
3
|
+
# admin chose to see there and the order they put them in. The available
|
|
4
|
+
# columns themselves are declared in the ActiveAdmin registration with the
|
|
5
|
+
# `configurable_columns` DSL.
|
|
5
6
|
#
|
|
6
7
|
# `admin` is polymorphic and its id column is a string, so any current-user
|
|
7
8
|
# model works, whatever its class and primary key type (integer, uuid, ...).
|
|
@@ -12,30 +13,44 @@ module ActiveadminConfigurableColumns
|
|
|
12
13
|
|
|
13
14
|
# Stored as JSON text rather than a database-specific json type, so the
|
|
14
15
|
# table works the same on every Active Record adapter. The array shape is
|
|
15
|
-
# enforced by the
|
|
16
|
+
# enforced by the validations below, not by the coder, so a bad assignment
|
|
16
17
|
# surfaces as a validation error instead of a serialization exception.
|
|
17
18
|
serialize :visible_columns, coder: JSON
|
|
19
|
+
# nil for rows saved before reordering existed: declaration order applies.
|
|
20
|
+
serialize :column_order, coder: JSON
|
|
18
21
|
|
|
19
22
|
after_initialize { self.visible_columns ||= [] if has_attribute?(:visible_columns) }
|
|
20
23
|
|
|
21
24
|
validates :resource_key, presence: true,
|
|
22
25
|
uniqueness: { scope: %i[admin_type admin_id], case_sensitive: false }
|
|
23
26
|
validate :visible_columns_must_be_an_array
|
|
27
|
+
validate :column_order_must_be_an_array_or_nil
|
|
24
28
|
|
|
25
|
-
# Returns the
|
|
29
|
+
# Returns the admin's SavedSelection for the table, or nil when they never
|
|
26
30
|
# saved anything (the caller then falls back to the declared defaults).
|
|
27
|
-
def self.
|
|
31
|
+
def self.fetch(admin, resource_key)
|
|
28
32
|
return nil if admin.blank?
|
|
29
33
|
|
|
30
|
-
find_by(admin: admin, resource_key: resource_key)
|
|
34
|
+
preference = find_by(admin: admin, resource_key: resource_key)
|
|
35
|
+
return nil if preference.nil?
|
|
36
|
+
|
|
37
|
+
SavedSelection.new(visible_keys: preference.visible_columns.map(&:to_sym),
|
|
38
|
+
order_keys: preference.column_order&.map(&:to_sym))
|
|
31
39
|
end
|
|
32
40
|
|
|
33
|
-
def self.store(admin:, resource_key:,
|
|
41
|
+
def self.store(admin:, resource_key:, visible_keys:, order_keys:)
|
|
34
42
|
preference = find_or_initialize_by(admin: admin, resource_key: resource_key)
|
|
35
|
-
preference.update!(visible_columns:
|
|
43
|
+
preference.update!(visible_columns: visible_keys.map(&:to_s), column_order: order_keys&.map(&:to_s))
|
|
36
44
|
preference
|
|
37
45
|
end
|
|
38
46
|
|
|
47
|
+
# Forgets the admin's selection, so the table falls back to the defaults.
|
|
48
|
+
def self.clear(admin:, resource_key:)
|
|
49
|
+
return if admin.blank?
|
|
50
|
+
|
|
51
|
+
where(admin: admin, resource_key: resource_key).delete_all
|
|
52
|
+
end
|
|
53
|
+
|
|
39
54
|
private
|
|
40
55
|
|
|
41
56
|
def visible_columns_must_be_an_array
|
|
@@ -43,5 +58,11 @@ module ActiveadminConfigurableColumns
|
|
|
43
58
|
|
|
44
59
|
errors.add(:visible_columns, :invalid)
|
|
45
60
|
end
|
|
61
|
+
|
|
62
|
+
def column_order_must_be_an_array_or_nil
|
|
63
|
+
return if column_order.nil? || column_order.is_a?(Array)
|
|
64
|
+
|
|
65
|
+
errors.add(:column_order, :invalid)
|
|
66
|
+
end
|
|
46
67
|
end
|
|
47
68
|
end
|
|
@@ -1,25 +1,40 @@
|
|
|
1
|
-
<%#
|
|
2
|
-
on the "Table columns" page, so it always posts back to the
|
|
1
|
+
<%# Sortable checkbox picker for one index table. Rendered both in the index
|
|
2
|
+
sidebar and on the "Table columns" page, so it always posts back to the
|
|
3
|
+
same action. Each row's hidden `columns[]` input carries the order. %>
|
|
3
4
|
<% registry = resource.column_registry %>
|
|
4
|
-
<%
|
|
5
|
+
<% selection = ActiveadminConfigurableColumns.selection_for(registry, current_active_admin_user) %>
|
|
5
6
|
<% update_path = send(ActiveadminConfigurableColumns.route_name(active_admin_namespace, :update)) %>
|
|
6
7
|
|
|
7
8
|
<%= form_with url: update_path, method: :patch, class: 'filters-form' do %>
|
|
8
9
|
<%= hidden_field_tag :resource_key, registry.resource_key, id: nil %>
|
|
9
|
-
<%# Keeps the parameter present when every box is unchecked, which restores the defaults. %>
|
|
10
|
-
<%= hidden_field_tag 'visible_columns[]', '', id: nil %>
|
|
11
10
|
|
|
12
|
-
<
|
|
13
|
-
<%
|
|
11
|
+
<ul data-aacc-list class="flex flex-col gap-1 mb-4 [&>.aacc-ghost]:opacity-40 [&>.aacc-chosen]:bg-gray-100 dark:[&>.aacc-chosen]:bg-gray-800">
|
|
12
|
+
<% selection.columns.each do |column| %>
|
|
14
13
|
<% checkbox_id = "#{registry.resource_key}_column_#{column.key}" %>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<%=
|
|
18
|
-
|
|
14
|
+
<% label = column.human_label(resource.resource_class) %>
|
|
15
|
+
<li data-aacc-row class="flex items-center gap-2 rounded">
|
|
16
|
+
<%= hidden_field_tag 'columns[]', column.key, id: nil %>
|
|
17
|
+
<button type="button" data-aacc-handle
|
|
18
|
+
class="inline-flex cursor-grab touch-none select-none rounded p-0.5 text-gray-400 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 dark:hover:text-gray-200"
|
|
19
|
+
aria-label="<%= t('active_admin.table_columns.move', column: label) %>"
|
|
20
|
+
title="<%= t('active_admin.table_columns.reorder_hint') %>">
|
|
21
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 16 16" fill="currentColor">
|
|
22
|
+
<circle cx="5.5" cy="3.5" r="1.5"/><circle cx="10.5" cy="3.5" r="1.5"/>
|
|
23
|
+
<circle cx="5.5" cy="8" r="1.5"/><circle cx="10.5" cy="8" r="1.5"/>
|
|
24
|
+
<circle cx="5.5" cy="12.5" r="1.5"/><circle cx="10.5" cy="12.5" r="1.5"/>
|
|
25
|
+
</svg>
|
|
26
|
+
</button>
|
|
27
|
+
<%= check_box_tag 'visible_columns[]', column.key, selection.visible?(column), id: checkbox_id %>
|
|
28
|
+
<%= label_tag checkbox_id, label, class: 'text-sm' %>
|
|
29
|
+
</li>
|
|
19
30
|
<% end %>
|
|
20
|
-
</
|
|
31
|
+
</ul>
|
|
21
32
|
|
|
22
33
|
<div class="filters-form-buttons">
|
|
23
34
|
<%= submit_tag t('active_admin.table_columns.save'), class: 'filters-form-submit' %>
|
|
35
|
+
<%= button_tag t('active_admin.table_columns.reset'), name: 'reset', value: '1',
|
|
36
|
+
class: 'text-sm text-gray-600 underline hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100' %>
|
|
24
37
|
</div>
|
|
25
38
|
<% end %>
|
|
39
|
+
|
|
40
|
+
<%= render 'activeadmin_configurable_columns/script' %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# Loads the picker script once per page, however many pickers it shows.
|
|
2
|
+
Needs importmap-rails (ActiveAdmin 4's default); see the README otherwise. %>
|
|
3
|
+
<% if respond_to?(:javascript_import_module_tag) && !@activeadmin_configurable_columns_script %>
|
|
4
|
+
<% @activeadmin_configurable_columns_script = true %>
|
|
5
|
+
<%= javascript_import_module_tag 'activeadmin_configurable_columns/picker' %>
|
|
6
|
+
<% end %>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Pinned into ActiveAdmin's importmap (not the application's) by the engine.
|
|
2
|
+
# Not preloaded: the picker is only needed on pages that render one.
|
|
3
|
+
pin 'activeadmin_configurable_columns/sortable', to: 'activeadmin_configurable_columns/sortable.js', preload: false
|
|
4
|
+
pin 'activeadmin_configurable_columns/picker', to: 'activeadmin_configurable_columns/picker.js', preload: false
|
data/config/locales/en.yml
CHANGED
|
@@ -8,3 +8,7 @@ en:
|
|
|
8
8
|
saved: "Columns saved"
|
|
9
9
|
empty: "No table has configurable columns yet."
|
|
10
10
|
unknown_resource: "Unknown table"
|
|
11
|
+
reset: "Reset to defaults"
|
|
12
|
+
reset_done: "Columns reset to defaults"
|
|
13
|
+
move: "Move %{column}"
|
|
14
|
+
reorder_hint: "Drag to reorder, or focus and use the arrow keys"
|
data/db/migrate/20261026000001_add_column_order_to_activeadmin_configurable_columns_preferences.rb
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class AddColumnOrderToActiveadminConfigurableColumnsPreferences < ActiveRecord::Migration[7.1]
|
|
2
|
+
def change
|
|
3
|
+
# JSON-serialized array of every declared column key, in the admin's order.
|
|
4
|
+
# NULL for selections saved before reordering existed: declaration order.
|
|
5
|
+
add_column :activeadmin_configurable_columns_preferences, :column_order, :text
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# The "Table columns" page: a central place to review and change visible table
|
|
2
|
-
# columns at once. The same form is rendered in the "Columns"
|
|
3
|
-
# configurable index, and both post back to the page's update
|
|
2
|
+
# columns and their order at once. The same form is rendered in the "Columns"
|
|
3
|
+
# sidebar of every configurable index, and both post back to the page's update
|
|
4
|
+
# action.
|
|
4
5
|
ActiveAdmin.register_page 'Table columns' do
|
|
5
6
|
menu ActiveadminConfigurableColumns.menu
|
|
6
7
|
|
|
@@ -20,13 +21,18 @@ ActiveAdmin.register_page 'Table columns' do
|
|
|
20
21
|
redirect_back fallback_location: page_path,
|
|
21
22
|
status: :see_other,
|
|
22
23
|
alert: t('active_admin.table_columns.unknown_resource')
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
elsif params[:reset].present?
|
|
25
|
+
ActiveadminConfigurableColumns.reset_selection(resource.column_registry, current_active_admin_user)
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
redirect_back fallback_location: page_path,
|
|
28
|
+
status: :see_other,
|
|
29
|
+
notice: t('active_admin.table_columns.reset_done')
|
|
30
|
+
else
|
|
31
|
+
ActiveadminConfigurableColumns.save_selection(
|
|
32
|
+
resource.column_registry,
|
|
33
|
+
current_active_admin_user,
|
|
34
|
+
visible: params[:visible_columns],
|
|
35
|
+
order: params[:columns]
|
|
30
36
|
)
|
|
31
37
|
|
|
32
38
|
redirect_back fallback_location: page_path,
|
|
@@ -17,5 +17,24 @@ module ActiveadminConfigurableColumns
|
|
|
17
17
|
ActiveAdmin.application.load_paths <<
|
|
18
18
|
Engine.root.join('lib', 'activeadmin_configurable_columns', 'admin').to_s
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# The picker script is pinned into ActiveAdmin's own importmap, the one its
|
|
22
|
+
# layout renders, mirroring how ActiveAdmin ships its JavaScript.
|
|
23
|
+
initializer 'activeadmin_configurable_columns.importmap', after: 'active_admin.importmap' do |app|
|
|
24
|
+
next unless app.config.respond_to?(:importmap)
|
|
25
|
+
|
|
26
|
+
ActiveAdmin.importmap.draw(Engine.root.join('config', 'importmap.rb'))
|
|
27
|
+
|
|
28
|
+
script_paths = [Engine.root.join('app', 'javascript'), Engine.root.join('vendor', 'javascript')]
|
|
29
|
+
app.config.assets.paths.concat(script_paths) if app.config.respond_to?(:assets)
|
|
30
|
+
|
|
31
|
+
# ActiveAdmin's sweeper watches only its own scripts; replace it with one
|
|
32
|
+
# that watches ours too, so edits are picked up without a restart.
|
|
33
|
+
if app.config.importmap.sweep_cache
|
|
34
|
+
ActiveAdmin.importmap.cache_sweeper(
|
|
35
|
+
watches: [ActiveAdmin::Engine.root.join('app', 'javascript'), Engine.root.join('app', 'javascript')]
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
20
39
|
end
|
|
21
40
|
end
|
|
@@ -12,6 +12,7 @@ module ActiveadminConfigurableColumns
|
|
|
12
12
|
def initialize(resource_key, columns)
|
|
13
13
|
@resource_key = resource_key.to_s
|
|
14
14
|
@columns = columns.freeze
|
|
15
|
+
@columns_by_key = columns.to_h { |column| [column.key, column] }.freeze
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def keys = columns.map(&:key)
|
|
@@ -30,6 +31,21 @@ module ActiveadminConfigurableColumns
|
|
|
30
31
|
|
|
31
32
|
def selected_keys(keys) = columns_for(keys).map(&:key)
|
|
32
33
|
|
|
34
|
+
def arrange(keys)
|
|
35
|
+
arranged = Array(keys).map { |key| key.to_s.to_sym }.uniq & self.keys
|
|
36
|
+
|
|
37
|
+
self.keys.each_with_index do |key, index|
|
|
38
|
+
next if arranged.include?(key)
|
|
39
|
+
|
|
40
|
+
previous = self.keys.first(index).reverse.find { |declared| arranged.include?(declared) }
|
|
41
|
+
arranged.insert(previous ? arranged.index(previous) + 1 : 0, key)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
arranged
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def columns_in(keys) = keys.filter_map { |key| @columns_by_key[key.to_sym] }
|
|
48
|
+
|
|
33
49
|
# Collects `column` declarations from a `configurable_columns` block.
|
|
34
50
|
class Builder
|
|
35
51
|
attr_reader :columns
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module ActiveadminConfigurableColumns
|
|
2
|
+
# What a preference store returns for one admin and one table.
|
|
3
|
+
#
|
|
4
|
+
# visible_keys -- the keys the admin ticked
|
|
5
|
+
# order_keys -- every key in the admin's order, or nil when the selection
|
|
6
|
+
# was saved before reordering existed (declaration order)
|
|
7
|
+
SavedSelection = Struct.new(:visible_keys, :order_keys, keyword_init: true)
|
|
8
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module ActiveadminConfigurableColumns
|
|
2
|
+
# The columns of one table as one admin sees them: every declared column in
|
|
3
|
+
# the admin's order, and which of them are visible. Both the index table and
|
|
4
|
+
# the picker read it, so they can never disagree.
|
|
5
|
+
class Selection
|
|
6
|
+
attr_reader :columns, :visible_keys
|
|
7
|
+
|
|
8
|
+
# `saved` is the store's SavedSelection, or nil when the admin never saved.
|
|
9
|
+
def initialize(registry, saved)
|
|
10
|
+
order = saved&.order_keys
|
|
11
|
+
@columns = registry.columns_in(order ? registry.arrange(order) : registry.keys)
|
|
12
|
+
@visible_keys = resolve_visible_keys(registry, saved).freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def visible_columns = columns.select { |column| visible?(column) }
|
|
16
|
+
|
|
17
|
+
def visible?(column) = visible_keys.include?(column.key)
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# A blank selection falls back to the defaults, so a table is never empty.
|
|
22
|
+
def resolve_visible_keys(registry, saved)
|
|
23
|
+
return registry.default_keys if saved.nil?
|
|
24
|
+
|
|
25
|
+
keys = registry.keys & Array(saved.visible_keys).map(&:to_sym)
|
|
26
|
+
keys |= added_since_saved(registry, saved.order_keys) if saved.order_keys
|
|
27
|
+
keys.empty? ? registry.default_keys : keys
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# A saved order lists every key declared at the time it was saved, so a key
|
|
31
|
+
# missing from it was declared later; it shows up when it is a default one.
|
|
32
|
+
# Selections saved without an order cannot tell new keys apart and keep
|
|
33
|
+
# showing exactly what was ticked.
|
|
34
|
+
def added_since_saved(registry, order_keys)
|
|
35
|
+
known = order_keys.map(&:to_sym)
|
|
36
|
+
|
|
37
|
+
registry.columns.select { |column| column.default? && !known.include?(column.key) }.map(&:key)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module ActiveadminConfigurableColumns
|
|
2
2
|
# Mixed into ActiveAdmin::Views::IndexAsTable::IndexTableFor, so that
|
|
3
3
|
# `configurable_columns` inside an `index` block renders the columns the
|
|
4
|
-
# current admin chose (falling back to the declared defaults).
|
|
4
|
+
# current admin chose, in their order (falling back to the declared defaults).
|
|
5
5
|
module TableRenderer
|
|
6
6
|
def configurable_columns
|
|
7
7
|
registry = active_admin_config.column_registry
|
|
@@ -11,7 +11,8 @@ module ActiveadminConfigurableColumns
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
ActiveadminConfigurableColumns
|
|
14
|
-
.
|
|
14
|
+
.selection_for(registry, helpers.current_active_admin_user)
|
|
15
|
+
.visible_columns
|
|
15
16
|
.each { |column| column.render_into(self) }
|
|
16
17
|
end
|
|
17
18
|
end
|
|
@@ -35,8 +35,9 @@ module ActiveadminConfigurableColumns
|
|
|
35
35
|
|
|
36
36
|
# Where saved selections live. Any object answering
|
|
37
37
|
#
|
|
38
|
-
#
|
|
39
|
-
# store(admin:, resource_key:,
|
|
38
|
+
# fetch(admin, resource_key) -> SavedSelection | nil (nil = never saved)
|
|
39
|
+
# store(admin:, resource_key:, visible_keys:, order_keys:) (persists a selection)
|
|
40
|
+
# clear(admin:, resource_key:) (back to the defaults)
|
|
40
41
|
#
|
|
41
42
|
# can be plugged in; the default is the bundled Preference model. Assigning
|
|
42
43
|
# nil restores that default.
|
|
@@ -59,11 +60,26 @@ module ActiveadminConfigurableColumns
|
|
|
59
60
|
{ label: proc { I18n.t('active_admin.table_columns.title') } }
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
# The columns the given admin
|
|
63
|
-
def
|
|
64
|
-
|
|
63
|
+
# The columns of this resource as the given admin arranged them.
|
|
64
|
+
def selection_for(registry, admin)
|
|
65
|
+
Selection.new(registry, preference_store.fetch(admin, registry.resource_key))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Persists what the admin submitted. Keys are normalized against the
|
|
69
|
+
# declaration; without an order the previously saved one is kept.
|
|
70
|
+
def save_selection(registry, admin, visible:, order: nil)
|
|
71
|
+
order = preference_store.fetch(admin, registry.resource_key)&.order_keys if order.nil?
|
|
72
|
+
|
|
73
|
+
preference_store.store(
|
|
74
|
+
admin: admin,
|
|
75
|
+
resource_key: registry.resource_key,
|
|
76
|
+
visible_keys: registry.selected_keys(visible),
|
|
77
|
+
order_keys: order && registry.arrange(order)
|
|
78
|
+
)
|
|
79
|
+
end
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
def reset_selection(registry, admin)
|
|
82
|
+
preference_store.clear(admin: admin, resource_key: registry.resource_key)
|
|
67
83
|
end
|
|
68
84
|
|
|
69
85
|
# Every resource of the namespace that declares configurable columns.
|
|
@@ -92,6 +108,8 @@ end
|
|
|
92
108
|
|
|
93
109
|
require_relative 'activeadmin_configurable_columns/column'
|
|
94
110
|
require_relative 'activeadmin_configurable_columns/registry'
|
|
111
|
+
require_relative 'activeadmin_configurable_columns/saved_selection'
|
|
112
|
+
require_relative 'activeadmin_configurable_columns/selection'
|
|
95
113
|
require_relative 'activeadmin_configurable_columns/dsl'
|
|
96
114
|
require_relative 'activeadmin_configurable_columns/resource_extension'
|
|
97
115
|
require_relative 'activeadmin_configurable_columns/table_renderer'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SortableJS 1.15.7 -- https://github.com/SortableJS/Sortable
|
|
3
|
+
* ES module build, downloaded from https://cdn.jsdelivr.net/npm/sortablejs@1.15.7/+esm
|
|
4
|
+
*
|
|
5
|
+
* MIT License. Copyright (c) 2019 All contributors to Sortable.
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software.
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
function ne(o,t){(t==null||t>o.length)&&(t=o.length);for(var e=0,n=Array(t);e<t;e++)n[e]=o[e];return n}function Ve(o){if(Array.isArray(o))return ne(o)}function Ze(o,t,e){return(t=rn(t))in o?Object.defineProperty(o,t,{value:e,enumerable:!0,configurable:!0,writable:!0}):o[t]=e,o}function U(){return U=Object.assign?Object.assign.bind():function(o){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var n in e)({}).hasOwnProperty.call(e,n)&&(o[n]=e[n])}return o},U.apply(null,arguments)}function Qe(o){if(typeof Symbol<"u"&&o[Symbol.iterator]!=null||o["@@iterator"]!=null)return Array.from(o)}function Je(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
|
23
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function _e(o,t){var e=Object.keys(o);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(o);t&&(n=n.filter(function(i){return Object.getOwnPropertyDescriptor(o,i).enumerable})),e.push.apply(e,n)}return e}function Z(o){for(var t=1;t<arguments.length;t++){var e=arguments[t]!=null?arguments[t]:{};t%2?_e(Object(e),!0).forEach(function(n){Ze(o,n,e[n])}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(e)):_e(Object(e)).forEach(function(n){Object.defineProperty(o,n,Object.getOwnPropertyDescriptor(e,n))})}return o}function tn(o,t){if(o==null)return{};var e,n,i=en(o,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(o);for(n=0;n<r.length;n++)e=r[n],t.indexOf(e)===-1&&{}.propertyIsEnumerable.call(o,e)&&(i[e]=o[e])}return i}function en(o,t){if(o==null)return{};var e={};for(var n in o)if({}.hasOwnProperty.call(o,n)){if(t.indexOf(n)!==-1)continue;e[n]=o[n]}return e}function nn(o){return Ve(o)||Qe(o)||an(o)||Je()}function on(o,t){if(typeof o!="object"||!o)return o;var e=o[Symbol.toPrimitive];if(e!==void 0){var n=e.call(o,t);if(typeof n!="object")return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(o)}function rn(o){var t=on(o,"string");return typeof t=="symbol"?t:t+""}function oe(o){"@babel/helpers - typeof";return oe=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},oe(o)}function an(o,t){if(o){if(typeof o=="string")return ne(o,t);var e={}.toString.call(o).slice(8,-1);return e==="Object"&&o.constructor&&(e=o.constructor.name),e==="Map"||e==="Set"?Array.from(o):e==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?ne(o,t):void 0}}var ln="1.15.7";function et(o){if(typeof window<"u"&&window.navigator)return!!navigator.userAgent.match(o)}var nt=et(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),Ct=et(/Edge/i),Ce=et(/firefox/i),Tt=et(/safari/i)&&!et(/chrome/i)&&!et(/android/i),ie=et(/iP(ad|od|hone)/i),Te=et(/chrome/i)&&et(/android/i),Oe={capture:!1,passive:!1};function y(o,t,e){o.addEventListener(t,e,!nt&&Oe)}function b(o,t,e){o.removeEventListener(t,e,!nt&&Oe)}function Ht(o,t){if(t){if(t[0]===">"&&(t=t.substring(1)),o)try{if(o.matches)return o.matches(t);if(o.msMatchesSelector)return o.msMatchesSelector(t);if(o.webkitMatchesSelector)return o.webkitMatchesSelector(t)}catch{return!1}return!1}}function Ae(o){return o.host&&o!==document&&o.host.nodeType&&o.host!==o?o.host:o.parentNode}function B(o,t,e,n){if(o){e=e||document;do{if(t!=null&&(t[0]===">"?o.parentNode===e&&Ht(o,t):Ht(o,t))||n&&o===e)return o;if(o===e)break}while(o=Ae(o))}return null}var Ie=/\s+/g;function A(o,t,e){if(o&&t)if(o.classList)o.classList[e?"add":"remove"](t);else{var n=(" "+o.className+" ").replace(Ie," ").replace(" "+t+" "," ");o.className=(n+(e?" "+t:"")).replace(Ie," ")}}function h(o,t,e){var n=o&&o.style;if(n){if(e===void 0)return document.defaultView&&document.defaultView.getComputedStyle?e=document.defaultView.getComputedStyle(o,""):o.currentStyle&&(e=o.currentStyle),t===void 0?e:e[t];!(t in n)&&t.indexOf("webkit")===-1&&(t="-webkit-"+t),n[t]=e+(typeof e=="string"?"":"px")}}function ct(o,t){var e="";if(typeof o=="string")e=o;else do{var n=h(o,"transform");n&&n!=="none"&&(e=n+" "+e)}while(!t&&(o=o.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(e)}function Ne(o,t,e){if(o){var n=o.getElementsByTagName(t),i=0,r=n.length;if(e)for(;i<r;i++)e(n[i],i);return n}return[]}function Q(){var o=document.scrollingElement;return o||document.documentElement}function T(o,t,e,n,i){if(!(!o.getBoundingClientRect&&o!==window)){var r,a,l,s,u,d,f;if(o!==window&&o.parentNode&&o!==Q()?(r=o.getBoundingClientRect(),a=r.top,l=r.left,s=r.bottom,u=r.right,d=r.height,f=r.width):(a=0,l=0,s=window.innerHeight,u=window.innerWidth,d=window.innerHeight,f=window.innerWidth),(t||e)&&o!==window&&(i=i||o.parentNode,!nt))do if(i&&i.getBoundingClientRect&&(h(i,"transform")!=="none"||e&&h(i,"position")!=="static")){var p=i.getBoundingClientRect();a-=p.top+parseInt(h(i,"border-top-width")),l-=p.left+parseInt(h(i,"border-left-width")),s=a+r.height,u=l+r.width;break}while(i=i.parentNode);if(n&&o!==window){var D=ct(i||o),w=D&&D.a,S=D&&D.d;D&&(a/=S,l/=w,f/=w,d/=S,s=a+d,u=l+f)}return{top:a,left:l,bottom:s,right:u,width:f,height:d}}}function Pe(o,t,e){for(var n=rt(o,!0),i=T(o)[t];n;){var r=T(n)[e],a=void 0;if(a=i>=r,!a)return n;if(n===Q())break;n=rt(n,!1)}return!1}function pt(o,t,e,n){for(var i=0,r=0,a=o.children;r<a.length;){if(a[r].style.display!=="none"&&a[r]!==g.ghost&&(n||a[r]!==g.dragged)&&B(a[r],e.draggable,o,!1)){if(i===t)return a[r];i++}r++}return null}function re(o,t){for(var e=o.lastElementChild;e&&(e===g.ghost||h(e,"display")==="none"||t&&!Ht(e,t));)e=e.previousElementSibling;return e||null}function N(o,t){var e=0;if(!o||!o.parentNode)return-1;for(;o=o.previousElementSibling;)o.nodeName.toUpperCase()!=="TEMPLATE"&&o!==g.clone&&(!t||Ht(o,t))&&e++;return e}function xe(o){var t=0,e=0,n=Q();if(o)do{var i=ct(o),r=i.a,a=i.d;t+=o.scrollLeft*r,e+=o.scrollTop*a}while(o!==n&&(o=o.parentNode));return[t,e]}function sn(o,t){for(var e in o)if(o.hasOwnProperty(e)){for(var n in t)if(t.hasOwnProperty(n)&&t[n]===o[e][n])return Number(e)}return-1}function rt(o,t){if(!o||!o.getBoundingClientRect)return Q();var e=o,n=!1;do if(e.clientWidth<e.scrollWidth||e.clientHeight<e.scrollHeight){var i=h(e);if(e.clientWidth<e.scrollWidth&&(i.overflowX=="auto"||i.overflowX=="scroll")||e.clientHeight<e.scrollHeight&&(i.overflowY=="auto"||i.overflowY=="scroll")){if(!e.getBoundingClientRect||e===document.body)return Q();if(n||t)return e;n=!0}}while(e=e.parentNode);return Q()}function un(o,t){if(o&&t)for(var e in t)t.hasOwnProperty(e)&&(o[e]=t[e]);return o}function ae(o,t){return Math.round(o.top)===Math.round(t.top)&&Math.round(o.left)===Math.round(t.left)&&Math.round(o.height)===Math.round(t.height)&&Math.round(o.width)===Math.round(t.width)}var Ot;function Me(o,t){return function(){if(!Ot){var e=arguments,n=this;e.length===1?o.call(n,e[0]):o.apply(n,e),Ot=setTimeout(function(){Ot=void 0},t)}}}function cn(){clearTimeout(Ot),Ot=void 0}function Fe(o,t,e){o.scrollLeft+=t,o.scrollTop+=e}function le(o){var t=window.Polymer,e=window.jQuery||window.Zepto;return t&&t.dom?t.dom(o).cloneNode(!0):e?e(o).clone(!0)[0]:o.cloneNode(!0)}function Re(o,t){h(o,"position","absolute"),h(o,"top",t.top),h(o,"left",t.left),h(o,"width",t.width),h(o,"height",t.height)}function se(o){h(o,"position",""),h(o,"top",""),h(o,"left",""),h(o,"width",""),h(o,"height","")}function ke(o,t,e){var n={};return Array.from(o.children).forEach(function(i){var r,a,l,s;if(!(!B(i,t.draggable,o,!1)||i.animated||i===e)){var u=T(i);n.left=Math.min((r=n.left)!==null&&r!==void 0?r:1/0,u.left),n.top=Math.min((a=n.top)!==null&&a!==void 0?a:1/0,u.top),n.right=Math.max((l=n.right)!==null&&l!==void 0?l:-1/0,u.right),n.bottom=Math.max((s=n.bottom)!==null&&s!==void 0?s:-1/0,u.bottom)}}),n.width=n.right-n.left,n.height=n.bottom-n.top,n.x=n.left,n.y=n.top,n}var F="Sortable"+new Date().getTime();function fn(){var o=[],t;return{captureAnimationState:function(){if(o=[],!!this.options.animation){var n=[].slice.call(this.el.children);n.forEach(function(i){if(!(h(i,"display")==="none"||i===g.ghost)){o.push({target:i,rect:T(i)});var r=Z({},o[o.length-1].rect);if(i.thisAnimationDuration){var a=ct(i,!0);a&&(r.top-=a.f,r.left-=a.e)}i.fromRect=r}})}},addAnimationState:function(n){o.push(n)},removeAnimationState:function(n){o.splice(sn(o,{target:n}),1)},animateAll:function(n){var i=this;if(!this.options.animation){clearTimeout(t),typeof n=="function"&&n();return}var r=!1,a=0;o.forEach(function(l){var s=0,u=l.target,d=u.fromRect,f=T(u),p=u.prevFromRect,D=u.prevToRect,w=l.rect,S=ct(u,!0);S&&(f.top-=S.f,f.left-=S.e),u.toRect=f,u.thisAnimationDuration&&ae(p,f)&&!ae(d,f)&&(w.top-f.top)/(w.left-f.left)===(d.top-f.top)/(d.left-f.left)&&(s=hn(w,p,D,i.options)),ae(f,d)||(u.prevFromRect=d,u.prevToRect=f,s||(s=i.options.animation),i.animate(u,w,f,s)),s&&(r=!0,a=Math.max(a,s),clearTimeout(u.animationResetTimer),u.animationResetTimer=setTimeout(function(){u.animationTime=0,u.prevFromRect=null,u.fromRect=null,u.prevToRect=null,u.thisAnimationDuration=null},s),u.thisAnimationDuration=s)}),clearTimeout(t),r?t=setTimeout(function(){typeof n=="function"&&n()},a):typeof n=="function"&&n(),o=[]},animate:function(n,i,r,a){if(a){h(n,"transition",""),h(n,"transform","");var l=ct(this.el),s=l&&l.a,u=l&&l.d,d=(i.left-r.left)/(s||1),f=(i.top-r.top)/(u||1);n.animatingX=!!d,n.animatingY=!!f,h(n,"transform","translate3d("+d+"px,"+f+"px,0)"),this.forRepaintDummy=dn(n),h(n,"transition","transform "+a+"ms"+(this.options.easing?" "+this.options.easing:"")),h(n,"transform","translate3d(0,0,0)"),typeof n.animated=="number"&&clearTimeout(n.animated),n.animated=setTimeout(function(){h(n,"transition",""),h(n,"transform",""),n.animated=!1,n.animatingX=!1,n.animatingY=!1},a)}}}}function dn(o){return o.offsetWidth}function hn(o,t,e,n){return Math.sqrt(Math.pow(t.top-o.top,2)+Math.pow(t.left-o.left,2))/Math.sqrt(Math.pow(t.top-e.top,2)+Math.pow(t.left-e.left,2))*n.animation}var gt=[],ue={initializeByDefault:!0},At={mount:function(t){for(var e in ue)ue.hasOwnProperty(e)&&!(e in t)&&(t[e]=ue[e]);gt.forEach(function(n){if(n.pluginName===t.pluginName)throw"Sortable: Cannot mount plugin ".concat(t.pluginName," more than once")}),gt.push(t)},pluginEvent:function(t,e,n){var i=this;this.eventCanceled=!1,n.cancel=function(){i.eventCanceled=!0};var r=t+"Global";gt.forEach(function(a){e[a.pluginName]&&(e[a.pluginName][r]&&e[a.pluginName][r](Z({sortable:e},n)),e.options[a.pluginName]&&e[a.pluginName][t]&&e[a.pluginName][t](Z({sortable:e},n)))})},initializePlugins:function(t,e,n,i){gt.forEach(function(l){var s=l.pluginName;if(!(!t.options[s]&&!l.initializeByDefault)){var u=new l(t,e,t.options);u.sortable=t,u.options=t.options,t[s]=u,U(n,u.defaults)}});for(var r in t.options)if(t.options.hasOwnProperty(r)){var a=this.modifyOption(t,r,t.options[r]);typeof a<"u"&&(t.options[r]=a)}},getEventProperties:function(t,e){var n={};return gt.forEach(function(i){typeof i.eventProperties=="function"&&U(n,i.eventProperties.call(e[i.pluginName],t))}),n},modifyOption:function(t,e,n){var i;return gt.forEach(function(r){t[r.pluginName]&&r.optionListeners&&typeof r.optionListeners[e]=="function"&&(i=r.optionListeners[e].call(t[r.pluginName],n))}),i}};function It(o){var t=o.sortable,e=o.rootEl,n=o.name,i=o.targetEl,r=o.cloneEl,a=o.toEl,l=o.fromEl,s=o.oldIndex,u=o.newIndex,d=o.oldDraggableIndex,f=o.newDraggableIndex,p=o.originalEvent,D=o.putSortable,w=o.extraEventProperties;if(t=t||e&&e[F],!!t){var S,X=t.options,W="on"+n.charAt(0).toUpperCase()+n.substr(1);window.CustomEvent&&!nt&&!Ct?S=new CustomEvent(n,{bubbles:!0,cancelable:!0}):(S=document.createEvent("Event"),S.initEvent(n,!0,!0)),S.to=a||e,S.from=l||e,S.item=i||e,S.clone=r,S.oldIndex=s,S.newIndex=u,S.oldDraggableIndex=d,S.newDraggableIndex=f,S.originalEvent=p,S.pullMode=D?D.lastPutMode:void 0;var E=Z(Z({},w),At.getEventProperties(n,t));for(var _ in E)S[_]=E[_];e&&e.dispatchEvent(S),X[W]&&X[W].call(t,S)}}var pn=["evt"],H=function(t,e){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},i=n.evt,r=tn(n,pn);At.pluginEvent.bind(g)(t,e,Z({dragEl:c,parentEl:P,ghostEl:m,rootEl:O,nextEl:ft,lastDownEl:Gt,cloneEl:I,cloneHidden:at,dragStarted:Pt,putSortable:R,activeSortable:g.active,originalEvent:i,oldIndex:mt,oldDraggableIndex:Nt,newIndex:K,newDraggableIndex:lt,hideGhostForTarget:Ke,unhideGhostForTarget:Le,cloneNowHidden:function(){at=!0},cloneNowShown:function(){at=!1},dispatchSortableEvent:function(l){Y({sortable:e,name:l,originalEvent:i})}},r))};function Y(o){It(Z({putSortable:R,cloneEl:I,targetEl:c,rootEl:O,oldIndex:mt,oldDraggableIndex:Nt,newIndex:K,newDraggableIndex:lt},o))}var c,P,m,O,ft,Gt,I,at,mt,K,Nt,lt,jt,R,vt=!1,Kt=!1,Lt=[],dt,q,ce,fe,Xe,Ye,Pt,bt,xt,Mt=!1,Wt=!1,zt,k,de=[],he=!1,Ut=[],$t=typeof document<"u",qt=ie,Be=Ct||nt?"cssFloat":"float",gn=$t&&!Te&&!ie&&"draggable"in document.createElement("div"),He=(function(){if($t){if(nt)return!1;var o=document.createElement("x");return o.style.cssText="pointer-events:auto",o.style.pointerEvents==="auto"}})(),Ge=function(t,e){var n=h(t),i=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),r=pt(t,0,e),a=pt(t,1,e),l=r&&h(r),s=a&&h(a),u=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+T(r).width,d=s&&parseInt(s.marginLeft)+parseInt(s.marginRight)+T(a).width;if(n.display==="flex")return n.flexDirection==="column"||n.flexDirection==="column-reverse"?"vertical":"horizontal";if(n.display==="grid")return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(r&&l.float&&l.float!=="none"){var f=l.float==="left"?"left":"right";return a&&(s.clear==="both"||s.clear===f)?"vertical":"horizontal"}return r&&(l.display==="block"||l.display==="flex"||l.display==="table"||l.display==="grid"||u>=i&&n[Be]==="none"||a&&n[Be]==="none"&&u+d>i)?"vertical":"horizontal"},mn=function(t,e,n){var i=n?t.left:t.top,r=n?t.right:t.bottom,a=n?t.width:t.height,l=n?e.left:e.top,s=n?e.right:e.bottom,u=n?e.width:e.height;return i===l||r===s||i+a/2===l+u/2},vn=function(t,e){var n;return Lt.some(function(i){var r=i[F].options.emptyInsertThreshold;if(!(!r||re(i))){var a=T(i),l=t>=a.left-r&&t<=a.right+r,s=e>=a.top-r&&e<=a.bottom+r;if(l&&s)return n=i}}),n},je=function(t){function e(r,a){return function(l,s,u,d){var f=l.options.group.name&&s.options.group.name&&l.options.group.name===s.options.group.name;if(r==null&&(a||f))return!0;if(r==null||r===!1)return!1;if(a&&r==="clone")return r;if(typeof r=="function")return e(r(l,s,u,d),a)(l,s,u,d);var p=(a?l:s).options.group.name;return r===!0||typeof r=="string"&&r===p||r.join&&r.indexOf(p)>-1}}var n={},i=t.group;(!i||oe(i)!="object")&&(i={name:i}),n.name=i.name,n.checkPull=e(i.pull,!0),n.checkPut=e(i.put),n.revertClone=i.revertClone,t.group=n},Ke=function(){!He&&m&&h(m,"display","none")},Le=function(){!He&&m&&h(m,"display","")};$t&&!Te&&document.addEventListener("click",function(o){if(Kt)return o.preventDefault(),o.stopPropagation&&o.stopPropagation(),o.stopImmediatePropagation&&o.stopImmediatePropagation(),Kt=!1,!1},!0);var ht=function(t){if(c){t=t.touches?t.touches[0]:t;var e=vn(t.clientX,t.clientY);if(e){var n={};for(var i in t)t.hasOwnProperty(i)&&(n[i]=t[i]);n.target=n.rootEl=e,n.preventDefault=void 0,n.stopPropagation=void 0,e[F]._onDragOver(n)}}},bn=function(t){c&&c.parentNode[F]._isOutsideThisEl(t.target)};function g(o,t){if(!(o&&o.nodeType&&o.nodeType===1))throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(o));this.el=o,this.options=t=U({},t),o[F]=this;var e={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(o.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Ge(o,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(a,l){a.setData("Text",l.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:g.supportPointer!==!1&&"PointerEvent"in window&&(!Tt||ie),emptyInsertThreshold:5};At.initializePlugins(this,o,e);for(var n in e)!(n in t)&&(t[n]=e[n]);je(t);for(var i in this)i.charAt(0)==="_"&&typeof this[i]=="function"&&(this[i]=this[i].bind(this));this.nativeDraggable=t.forceFallback?!1:gn,this.nativeDraggable&&(this.options.touchStartThreshold=1),t.supportPointer?y(o,"pointerdown",this._onTapStart):(y(o,"mousedown",this._onTapStart),y(o,"touchstart",this._onTapStart)),this.nativeDraggable&&(y(o,"dragover",this),y(o,"dragenter",this)),Lt.push(this.el),t.store&&t.store.get&&this.sort(t.store.get(this)||[]),U(this,fn())}g.prototype={constructor:g,_isOutsideThisEl:function(t){!this.el.contains(t)&&t!==this.el&&(bt=null)},_getDirection:function(t,e){return typeof this.options.direction=="function"?this.options.direction.call(this,t,e,c):this.options.direction},_onTapStart:function(t){if(t.cancelable){var e=this,n=this.el,i=this.options,r=i.preventOnFilter,a=t.type,l=t.touches&&t.touches[0]||t.pointerType&&t.pointerType==="touch"&&t,s=(l||t).target,u=t.target.shadowRoot&&(t.path&&t.path[0]||t.composedPath&&t.composedPath()[0])||s,d=i.filter;if(Tn(n),!c&&!(/mousedown|pointerdown/.test(a)&&t.button!==0||i.disabled)&&!u.isContentEditable&&!(!this.nativeDraggable&&Tt&&s&&s.tagName.toUpperCase()==="SELECT")&&(s=B(s,i.draggable,n,!1),!(s&&s.animated)&&Gt!==s)){if(mt=N(s),Nt=N(s,i.draggable),typeof d=="function"){if(d.call(this,t,s,this)){Y({sortable:e,rootEl:u,name:"filter",targetEl:s,toEl:n,fromEl:n}),H("filter",e,{evt:t}),r&&t.preventDefault();return}}else if(d&&(d=d.split(",").some(function(f){if(f=B(u,f.trim(),n,!1),f)return Y({sortable:e,rootEl:f,name:"filter",targetEl:s,fromEl:n,toEl:n}),H("filter",e,{evt:t}),!0}),d)){r&&t.preventDefault();return}i.handle&&!B(u,i.handle,n,!1)||this._prepareDragStart(t,l,s)}}},_prepareDragStart:function(t,e,n){var i=this,r=i.el,a=i.options,l=r.ownerDocument,s;if(n&&!c&&n.parentNode===r){var u=T(n);if(O=r,c=n,P=c.parentNode,ft=c.nextSibling,Gt=n,jt=a.group,g.dragged=c,dt={target:c,clientX:(e||t).clientX,clientY:(e||t).clientY},Xe=dt.clientX-u.left,Ye=dt.clientY-u.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,c.style["will-change"]="all",s=function(){if(H("delayEnded",i,{evt:t}),g.eventCanceled){i._onDrop();return}i._disableDelayedDragEvents(),!Ce&&i.nativeDraggable&&(c.draggable=!0),i._triggerDragStart(t,e),Y({sortable:i,name:"choose",originalEvent:t}),A(c,a.chosenClass,!0)},a.ignore.split(",").forEach(function(d){Ne(c,d.trim(),pe)}),y(l,"dragover",ht),y(l,"mousemove",ht),y(l,"touchmove",ht),a.supportPointer?(y(l,"pointerup",i._onDrop),!this.nativeDraggable&&y(l,"pointercancel",i._onDrop)):(y(l,"mouseup",i._onDrop),y(l,"touchend",i._onDrop),y(l,"touchcancel",i._onDrop)),Ce&&this.nativeDraggable&&(this.options.touchStartThreshold=4,c.draggable=!0),H("delayStart",this,{evt:t}),a.delay&&(!a.delayOnTouchOnly||e)&&(!this.nativeDraggable||!(Ct||nt))){if(g.eventCanceled){this._onDrop();return}a.supportPointer?(y(l,"pointerup",i._disableDelayedDrag),y(l,"pointercancel",i._disableDelayedDrag)):(y(l,"mouseup",i._disableDelayedDrag),y(l,"touchend",i._disableDelayedDrag),y(l,"touchcancel",i._disableDelayedDrag)),y(l,"mousemove",i._delayedDragTouchMoveHandler),y(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&y(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(s,a.delay)}else s()}},_delayedDragTouchMoveHandler:function(t){var e=t.touches?t.touches[0]:t;Math.max(Math.abs(e.clientX-this._lastX),Math.abs(e.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){c&&pe(c),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;b(t,"mouseup",this._disableDelayedDrag),b(t,"touchend",this._disableDelayedDrag),b(t,"touchcancel",this._disableDelayedDrag),b(t,"pointerup",this._disableDelayedDrag),b(t,"pointercancel",this._disableDelayedDrag),b(t,"mousemove",this._delayedDragTouchMoveHandler),b(t,"touchmove",this._delayedDragTouchMoveHandler),b(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||t.pointerType=="touch"&&t,!this.nativeDraggable||e?this.options.supportPointer?y(document,"pointermove",this._onTouchMove):e?y(document,"touchmove",this._onTouchMove):y(document,"mousemove",this._onTouchMove):(y(c,"dragend",this),y(O,"dragstart",this._onDragStart));try{document.selection?Zt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch{}},_dragStarted:function(t,e){if(vt=!1,O&&c){H("dragStarted",this,{evt:e}),this.nativeDraggable&&y(document,"dragover",bn);var n=this.options;!t&&A(c,n.dragClass,!1),A(c,n.ghostClass,!0),g.active=this,t&&this._appendGhost(),Y({sortable:this,name:"start",originalEvent:e})}else this._nulling()},_emulateDragOver:function(){if(q){this._lastX=q.clientX,this._lastY=q.clientY,Ke();for(var t=document.elementFromPoint(q.clientX,q.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(q.clientX,q.clientY),t!==e);)e=t;if(c.parentNode[F]._isOutsideThisEl(t),e)do{if(e[F]){var n=void 0;if(n=e[F]._onDragOver({clientX:q.clientX,clientY:q.clientY,target:t,rootEl:e}),n&&!this.options.dragoverBubble)break}t=e}while(e=Ae(e));Le()}},_onTouchMove:function(t){if(dt){var e=this.options,n=e.fallbackTolerance,i=e.fallbackOffset,r=t.touches?t.touches[0]:t,a=m&&ct(m,!0),l=m&&a&&a.a,s=m&&a&&a.d,u=qt&&k&&xe(k),d=(r.clientX-dt.clientX+i.x)/(l||1)+(u?u[0]-de[0]:0)/(l||1),f=(r.clientY-dt.clientY+i.y)/(s||1)+(u?u[1]-de[1]:0)/(s||1);if(!g.active&&!vt){if(n&&Math.max(Math.abs(r.clientX-this._lastX),Math.abs(r.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}if(m){a?(a.e+=d-(ce||0),a.f+=f-(fe||0)):a={a:1,b:0,c:0,d:1,e:d,f};var p="matrix(".concat(a.a,",").concat(a.b,",").concat(a.c,",").concat(a.d,",").concat(a.e,",").concat(a.f,")");h(m,"webkitTransform",p),h(m,"mozTransform",p),h(m,"msTransform",p),h(m,"transform",p),ce=d,fe=f,q=r}t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!m){var t=this.options.fallbackOnBody?document.body:O,e=T(c,!0,qt,!0,t),n=this.options;if(qt){for(k=t;h(k,"position")==="static"&&h(k,"transform")==="none"&&k!==document;)k=k.parentNode;k!==document.body&&k!==document.documentElement?(k===document&&(k=Q()),e.top+=k.scrollTop,e.left+=k.scrollLeft):k=Q(),de=xe(k)}m=c.cloneNode(!0),A(m,n.ghostClass,!1),A(m,n.fallbackClass,!0),A(m,n.dragClass,!0),h(m,"transition",""),h(m,"transform",""),h(m,"box-sizing","border-box"),h(m,"margin",0),h(m,"top",e.top),h(m,"left",e.left),h(m,"width",e.width),h(m,"height",e.height),h(m,"opacity","0.8"),h(m,"position",qt?"absolute":"fixed"),h(m,"zIndex","100000"),h(m,"pointerEvents","none"),g.ghost=m,t.appendChild(m),h(m,"transform-origin",Xe/parseInt(m.style.width)*100+"% "+Ye/parseInt(m.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,i=t.dataTransfer,r=n.options;if(H("dragStart",this,{evt:t}),g.eventCanceled){this._onDrop();return}H("setupClone",this),g.eventCanceled||(I=le(c),I.removeAttribute("id"),I.draggable=!1,I.style["will-change"]="",this._hideClone(),A(I,this.options.chosenClass,!1),g.clone=I),n.cloneId=Zt(function(){H("clone",n),!g.eventCanceled&&(n.options.removeCloneOnHide||O.insertBefore(I,c),n._hideClone(),Y({sortable:n,name:"clone"}))}),!e&&A(c,r.dragClass,!0),e?(Kt=!0,n._loopId=setInterval(n._emulateDragOver,50)):(b(document,"mouseup",n._onDrop),b(document,"touchend",n._onDrop),b(document,"touchcancel",n._onDrop),i&&(i.effectAllowed="move",r.setData&&r.setData.call(n,i,c)),y(document,"drop",n),h(c,"transform","translateZ(0)")),vt=!0,n._dragStartId=Zt(n._dragStarted.bind(n,e,t)),y(document,"selectstart",n),Pt=!0,window.getSelection().removeAllRanges(),Tt&&h(document.body,"user-select","none")},_onDragOver:function(t){var e=this.el,n=t.target,i,r,a,l=this.options,s=l.group,u=g.active,d=jt===s,f=l.sort,p=R||u,D,w=this,S=!1;if(he)return;function X(_t,$e){H(_t,w,Z({evt:t,isOwner:d,axis:D?"vertical":"horizontal",revert:a,dragRect:i,targetRect:r,canSort:f,fromSortable:p,target:n,completed:E,onMove:function(Se,qe){return Vt(O,e,c,i,Se,T(Se),t,qe)},changed:_},$e))}function W(){X("dragOverAnimationCapture"),w.captureAnimationState(),w!==p&&p.captureAnimationState()}function E(_t){return X("dragOverCompleted",{insertion:_t}),_t&&(d?u._hideClone():u._showClone(w),w!==p&&(A(c,R?R.options.ghostClass:u.options.ghostClass,!1),A(c,l.ghostClass,!0)),R!==w&&w!==g.active?R=w:w===g.active&&R&&(R=null),p===w&&(w._ignoreWhileAnimating=n),w.animateAll(function(){X("dragOverAnimationComplete"),w._ignoreWhileAnimating=null}),w!==p&&(p.animateAll(),p._ignoreWhileAnimating=null)),(n===c&&!c.animated||n===e&&!n.animated)&&(bt=null),!l.dragoverBubble&&!t.rootEl&&n!==document&&(c.parentNode[F]._isOutsideThisEl(t.target),!_t&&ht(t)),!l.dragoverBubble&&t.stopPropagation&&t.stopPropagation(),S=!0}function _(){K=N(c),lt=N(c,l.draggable),Y({sortable:w,name:"change",toEl:e,newIndex:K,newDraggableIndex:lt,originalEvent:t})}if(t.preventDefault!==void 0&&t.cancelable&&t.preventDefault(),n=B(n,l.draggable,e,!0),X("dragOver"),g.eventCanceled)return S;if(c.contains(t.target)||n.animated&&n.animatingX&&n.animatingY||w._ignoreWhileAnimating===n)return E(!1);if(Kt=!1,u&&!l.disabled&&(d?f||(a=P!==O):R===this||(this.lastPutMode=jt.checkPull(this,u,c,t))&&s.checkPut(this,u,c,t))){if(D=this._getDirection(t,n)==="vertical",i=T(c),X("dragOverValid"),g.eventCanceled)return S;if(a)return P=O,W(),this._hideClone(),X("revert"),g.eventCanceled||(ft?O.insertBefore(c,ft):O.appendChild(c)),E(!0);var M=re(e,l.draggable);if(!M||Dn(t,D,this)&&!M.animated){if(M===c)return E(!1);if(M&&e===t.target&&(n=M),n&&(r=T(n)),Vt(O,e,c,i,n,r,t,!!n)!==!1)return W(),M&&M.nextSibling?e.insertBefore(c,M.nextSibling):e.appendChild(c),P=e,_(),E(!0)}else if(M&&En(t,D,this)){var J=pt(e,0,l,!0);if(J===c)return E(!1);if(n=J,r=T(n),Vt(O,e,c,i,n,r,t,!1)!==!1)return W(),e.insertBefore(c,J),P=e,_(),E(!0)}else if(n.parentNode===e){r=T(n);var z=0,st,wt=c.parentNode!==e,j=!mn(c.animated&&c.toRect||i,n.animated&&n.toRect||r,D),Et=D?"top":"left",ot=Pe(n,"top","top")||Pe(c,"top","top"),Dt=ot?ot.scrollTop:void 0;bt!==n&&(st=r[Et],Mt=!1,Wt=!j&&l.invertSwap||wt),z=Sn(t,n,r,D,j?1:l.swapThreshold,l.invertedSwapThreshold==null?l.swapThreshold:l.invertedSwapThreshold,Wt,bt===n);var tt;if(z!==0){var ut=N(c);do ut-=z,tt=P.children[ut];while(tt&&(h(tt,"display")==="none"||tt===m))}if(z===0||tt===n)return E(!1);bt=n,xt=z;var St=n.nextElementSibling,it=!1;it=z===1;var Bt=Vt(O,e,c,i,n,r,t,it);if(Bt!==!1)return(Bt===1||Bt===-1)&&(it=Bt===1),he=!0,setTimeout(wn,30),W(),it&&!St?e.appendChild(c):n.parentNode.insertBefore(c,it?St:n),ot&&Fe(ot,0,Dt-ot.scrollTop),P=c.parentNode,st!==void 0&&!Wt&&(zt=Math.abs(st-T(n)[Et])),_(),E(!0)}if(e.contains(c))return E(!1)}return!1},_ignoreWhileAnimating:null,_offMoveEvents:function(){b(document,"mousemove",this._onTouchMove),b(document,"touchmove",this._onTouchMove),b(document,"pointermove",this._onTouchMove),b(document,"dragover",ht),b(document,"mousemove",ht),b(document,"touchmove",ht)},_offUpEvents:function(){var t=this.el.ownerDocument;b(t,"mouseup",this._onDrop),b(t,"touchend",this._onDrop),b(t,"pointerup",this._onDrop),b(t,"pointercancel",this._onDrop),b(t,"touchcancel",this._onDrop),b(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;if(K=N(c),lt=N(c,n.draggable),H("drop",this,{evt:t}),P=c&&c.parentNode,K=N(c),lt=N(c,n.draggable),g.eventCanceled){this._nulling();return}vt=!1,Wt=!1,Mt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),ge(this.cloneId),ge(this._dragStartId),this.nativeDraggable&&(b(document,"drop",this),b(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),Tt&&h(document.body,"user-select",""),h(c,"transform",""),t&&(Pt&&(t.cancelable&&t.preventDefault(),!n.dropBubble&&t.stopPropagation()),m&&m.parentNode&&m.parentNode.removeChild(m),(O===P||R&&R.lastPutMode!=="clone")&&I&&I.parentNode&&I.parentNode.removeChild(I),c&&(this.nativeDraggable&&b(c,"dragend",this),pe(c),c.style["will-change"]="",Pt&&!vt&&A(c,R?R.options.ghostClass:this.options.ghostClass,!1),A(c,this.options.chosenClass,!1),Y({sortable:this,name:"unchoose",toEl:P,newIndex:null,newDraggableIndex:null,originalEvent:t}),O!==P?(K>=0&&(Y({rootEl:P,name:"add",toEl:P,fromEl:O,originalEvent:t}),Y({sortable:this,name:"remove",toEl:P,originalEvent:t}),Y({rootEl:P,name:"sort",toEl:P,fromEl:O,originalEvent:t}),Y({sortable:this,name:"sort",toEl:P,originalEvent:t})),R&&R.save()):K!==mt&&K>=0&&(Y({sortable:this,name:"update",toEl:P,originalEvent:t}),Y({sortable:this,name:"sort",toEl:P,originalEvent:t})),g.active&&((K==null||K===-1)&&(K=mt,lt=Nt),Y({sortable:this,name:"end",toEl:P,originalEvent:t}),this.save()))),this._nulling()},_nulling:function(){H("nulling",this),O=c=P=m=ft=I=Gt=at=dt=q=Pt=K=lt=mt=Nt=bt=xt=R=jt=g.dragged=g.ghost=g.clone=g.active=null;var t=this.el;Ut.forEach(function(e){t.contains(e)&&(e.checked=!0)}),Ut.length=ce=fe=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":c&&(this._onDragOver(t),yn(t));break;case"selectstart":t.preventDefault();break}},toArray:function(){for(var t=[],e,n=this.el.children,i=0,r=n.length,a=this.options;i<r;i++)e=n[i],B(e,a.draggable,this.el,!1)&&t.push(e.getAttribute(a.dataIdAttr)||Cn(e));return t},sort:function(t,e){var n={},i=this.el;this.toArray().forEach(function(r,a){var l=i.children[a];B(l,this.options.draggable,i,!1)&&(n[r]=l)},this),e&&this.captureAnimationState(),t.forEach(function(r){n[r]&&(i.removeChild(n[r]),i.appendChild(n[r]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return B(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(e===void 0)return n[t];var i=At.modifyOption(this,t,e);typeof i<"u"?n[t]=i:n[t]=e,t==="group"&&je(n)},destroy:function(){H("destroy",this);var t=this.el;t[F]=null,b(t,"mousedown",this._onTapStart),b(t,"touchstart",this._onTapStart),b(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(b(t,"dragover",this),b(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(e){e.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Lt.splice(Lt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){if(!at){if(H("hideClone",this),g.eventCanceled)return;h(I,"display","none"),this.options.removeCloneOnHide&&I.parentNode&&I.parentNode.removeChild(I),at=!0}},_showClone:function(t){if(t.lastPutMode!=="clone"){this._hideClone();return}if(at){if(H("showClone",this),g.eventCanceled)return;c.parentNode==O&&!this.options.group.revertClone?O.insertBefore(I,c):ft?O.insertBefore(I,ft):O.appendChild(I),this.options.group.revertClone&&this.animate(c,I),h(I,"display",""),at=!1}}};function yn(o){o.dataTransfer&&(o.dataTransfer.dropEffect="move"),o.cancelable&&o.preventDefault()}function Vt(o,t,e,n,i,r,a,l){var s,u=o[F],d=u.options.onMove,f;return window.CustomEvent&&!nt&&!Ct?s=new CustomEvent("move",{bubbles:!0,cancelable:!0}):(s=document.createEvent("Event"),s.initEvent("move",!0,!0)),s.to=t,s.from=o,s.dragged=e,s.draggedRect=n,s.related=i||t,s.relatedRect=r||T(t),s.willInsertAfter=l,s.originalEvent=a,o.dispatchEvent(s),d&&(f=d.call(u,s,a)),f}function pe(o){o.draggable=!1}function wn(){he=!1}function En(o,t,e){var n=T(pt(e.el,0,e.options,!0)),i=ke(e.el,e.options,m),r=10;return t?o.clientX<i.left-r||o.clientY<n.top&&o.clientX<n.right:o.clientY<i.top-r||o.clientY<n.bottom&&o.clientX<n.left}function Dn(o,t,e){var n=T(re(e.el,e.options.draggable)),i=ke(e.el,e.options,m),r=10;return t?o.clientX>i.right+r||o.clientY>n.bottom&&o.clientX>n.left:o.clientY>i.bottom+r||o.clientX>n.right&&o.clientY>n.top}function Sn(o,t,e,n,i,r,a,l){var s=n?o.clientY:o.clientX,u=n?e.height:e.width,d=n?e.top:e.left,f=n?e.bottom:e.right,p=!1;if(!a){if(l&&zt<u*i){if(!Mt&&(xt===1?s>d+u*r/2:s<f-u*r/2)&&(Mt=!0),Mt)p=!0;else if(xt===1?s<d+zt:s>f-zt)return-xt}else if(s>d+u*(1-i)/2&&s<f-u*(1-i)/2)return _n(t)}return p=p||a,p&&(s<d+u*r/2||s>f-u*r/2)?s>d+u/2?1:-1:0}function _n(o){return N(c)<N(o)?1:-1}function Cn(o){for(var t=o.tagName+o.className+o.src+o.href+o.textContent,e=t.length,n=0;e--;)n+=t.charCodeAt(e);return n.toString(36)}function Tn(o){Ut.length=0;for(var t=o.getElementsByTagName("input"),e=t.length;e--;){var n=t[e];n.checked&&Ut.push(n)}}function Zt(o){return setTimeout(o,0)}function ge(o){return clearTimeout(o)}$t&&y(document,"touchmove",function(o){(g.active||vt)&&o.cancelable&&o.preventDefault()}),g.utils={on:y,off:b,css:h,find:Ne,is:function(t,e){return!!B(t,e,t,!1)},extend:un,throttle:Me,closest:B,toggleClass:A,clone:le,index:N,nextTick:Zt,cancelNextTick:ge,detectDirection:Ge,getChild:pt,expando:F},g.get=function(o){return o[F]},g.mount=function(){for(var o=arguments.length,t=new Array(o),e=0;e<o;e++)t[e]=arguments[e];t[0].constructor===Array&&(t=t[0]),t.forEach(function(n){if(!n.prototype||!n.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(n));n.utils&&(g.utils=Z(Z({},g.utils),n.utils)),At.mount(n)})},g.create=function(o,t){return new g(o,t)},g.version=ln;var x=[],Ft,me,ve=!1,be,ye,Qt,Rt;function On(){function o(){this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0};for(var t in this)t.charAt(0)==="_"&&typeof this[t]=="function"&&(this[t]=this[t].bind(this))}return o.prototype={dragStarted:function(e){var n=e.originalEvent;this.sortable.nativeDraggable?y(document,"dragover",this._handleAutoScroll):this.options.supportPointer?y(document,"pointermove",this._handleFallbackAutoScroll):n.touches?y(document,"touchmove",this._handleFallbackAutoScroll):y(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(e){var n=e.originalEvent;!this.options.dragOverBubble&&!n.rootEl&&this._handleAutoScroll(n)},drop:function(){this.sortable.nativeDraggable?b(document,"dragover",this._handleAutoScroll):(b(document,"pointermove",this._handleFallbackAutoScroll),b(document,"touchmove",this._handleFallbackAutoScroll),b(document,"mousemove",this._handleFallbackAutoScroll)),We(),Jt(),cn()},nulling:function(){Qt=me=Ft=ve=Rt=be=ye=null,x.length=0},_handleFallbackAutoScroll:function(e){this._handleAutoScroll(e,!0)},_handleAutoScroll:function(e,n){var i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,l=document.elementFromPoint(r,a);if(Qt=e,n||this.options.forceAutoScrollFallback||Ct||nt||Tt){we(e,this.options,l,n);var s=rt(l,!0);ve&&(!Rt||r!==be||a!==ye)&&(Rt&&We(),Rt=setInterval(function(){var u=rt(document.elementFromPoint(r,a),!0);u!==s&&(s=u,Jt()),we(e,i.options,u,n)},10),be=r,ye=a)}else{if(!this.options.bubbleScroll||rt(l,!0)===Q()){Jt();return}we(e,this.options,rt(l,!1),!1)}}},U(o,{pluginName:"scroll",initializeByDefault:!0})}function Jt(){x.forEach(function(o){clearInterval(o.pid)}),x=[]}function We(){clearInterval(Rt)}var we=Me(function(o,t,e,n){if(t.scroll){var i=(o.touches?o.touches[0]:o).clientX,r=(o.touches?o.touches[0]:o).clientY,a=t.scrollSensitivity,l=t.scrollSpeed,s=Q(),u=!1,d;me!==e&&(me=e,Jt(),Ft=t.scroll,d=t.scrollFn,Ft===!0&&(Ft=rt(e,!0)));var f=0,p=Ft;do{var D=p,w=T(D),S=w.top,X=w.bottom,W=w.left,E=w.right,_=w.width,M=w.height,J=void 0,z=void 0,st=D.scrollWidth,wt=D.scrollHeight,j=h(D),Et=D.scrollLeft,ot=D.scrollTop;D===s?(J=_<st&&(j.overflowX==="auto"||j.overflowX==="scroll"||j.overflowX==="visible"),z=M<wt&&(j.overflowY==="auto"||j.overflowY==="scroll"||j.overflowY==="visible")):(J=_<st&&(j.overflowX==="auto"||j.overflowX==="scroll"),z=M<wt&&(j.overflowY==="auto"||j.overflowY==="scroll"));var Dt=J&&(Math.abs(E-i)<=a&&Et+_<st)-(Math.abs(W-i)<=a&&!!Et),tt=z&&(Math.abs(X-r)<=a&&ot+M<wt)-(Math.abs(S-r)<=a&&!!ot);if(!x[f])for(var ut=0;ut<=f;ut++)x[ut]||(x[ut]={});(x[f].vx!=Dt||x[f].vy!=tt||x[f].el!==D)&&(x[f].el=D,x[f].vx=Dt,x[f].vy=tt,clearInterval(x[f].pid),(Dt!=0||tt!=0)&&(u=!0,x[f].pid=setInterval(function(){n&&this.layer===0&&g.active._onTouchMove(Qt);var St=x[this.layer].vy?x[this.layer].vy*l:0,it=x[this.layer].vx?x[this.layer].vx*l:0;typeof d=="function"&&d.call(g.dragged.parentNode[F],it,St,o,Qt,x[this.layer].el)!=="continue"||Fe(x[this.layer].el,it,St)}.bind({layer:f}),24))),f++}while(t.bubbleScroll&&p!==s&&(p=rt(p,!1)));ve=u}},30),ze=function(t){var e=t.originalEvent,n=t.putSortable,i=t.dragEl,r=t.activeSortable,a=t.dispatchSortableEvent,l=t.hideGhostForTarget,s=t.unhideGhostForTarget;if(e){var u=n||r;l();var d=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,f=document.elementFromPoint(d.clientX,d.clientY);s(),u&&!u.el.contains(f)&&(a("spill"),this.onSpill({dragEl:i,putSortable:n}))}};function Ee(){}Ee.prototype={startIndex:null,dragStart:function(t){var e=t.oldDraggableIndex;this.startIndex=e},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();var i=pt(this.sortable.el,this.startIndex,this.options);i?this.sortable.el.insertBefore(e,i):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:ze},U(Ee,{pluginName:"revertOnSpill"});function De(){}De.prototype={onSpill:function(t){var e=t.dragEl,n=t.putSortable,i=n||this.sortable;i.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),i.animateAll()},drop:ze},U(De,{pluginName:"removeOnSpill"});var $;function An(){function o(){this.defaults={swapClass:"sortable-swap-highlight"}}return o.prototype={dragStart:function(e){var n=e.dragEl;$=n},dragOverValid:function(e){var n=e.completed,i=e.target,r=e.onMove,a=e.activeSortable,l=e.changed,s=e.cancel;if(a.options.swap){var u=this.sortable.el,d=this.options;if(i&&i!==u){var f=$;r(i)!==!1?(A(i,d.swapClass,!0),$=i):$=null,f&&f!==$&&A(f,d.swapClass,!1)}l(),n(!0),s()}},drop:function(e){var n=e.activeSortable,i=e.putSortable,r=e.dragEl,a=i||this.sortable,l=this.options;$&&A($,l.swapClass,!1),$&&(l.swap||i&&i.options.swap)&&r!==$&&(a.captureAnimationState(),a!==n&&n.captureAnimationState(),In(r,$),a.animateAll(),a!==n&&n.animateAll())},nulling:function(){$=null}},U(o,{pluginName:"swap",eventProperties:function(){return{swapItem:$}}})}function In(o,t){var e=o.parentNode,n=t.parentNode,i,r;!e||!n||e.isEqualNode(t)||n.isEqualNode(o)||(i=N(o),r=N(t),e.isEqualNode(n)&&i<r&&r++,e.insertBefore(t,e.children[i]),n.insertBefore(o,n.children[r]))}var v=[],L=[],kt,V,Xt=!1,G=!1,yt=!1,C,Yt,te;function Nn(){function o(t){for(var e in this)e.charAt(0)==="_"&&typeof this[e]=="function"&&(this[e]=this[e].bind(this));t.options.avoidImplicitDeselect||(t.options.supportPointer?y(document,"pointerup",this._deselectMultiDrag):(y(document,"mouseup",this._deselectMultiDrag),y(document,"touchend",this._deselectMultiDrag))),y(document,"keydown",this._checkKeyDown),y(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(i,r){var a="";v.length&&V===t?v.forEach(function(l,s){a+=(s?", ":"")+l.textContent}):a=r.textContent,i.setData("Text",a)}}}return o.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(e){var n=e.dragEl;C=n},delayEnded:function(){this.isMultiDrag=~v.indexOf(C)},setupClone:function(e){var n=e.sortable,i=e.cancel;if(this.isMultiDrag){for(var r=0;r<v.length;r++)L.push(le(v[r])),L[r].sortableIndex=v[r].sortableIndex,L[r].draggable=!1,L[r].style["will-change"]="",A(L[r],this.options.selectedClass,!1),v[r]===C&&A(L[r],this.options.chosenClass,!1);n._hideClone(),i()}},clone:function(e){var n=e.sortable,i=e.rootEl,r=e.dispatchSortableEvent,a=e.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||v.length&&V===n&&(Ue(!0,i),r("clone"),a()))},showClone:function(e){var n=e.cloneNowShown,i=e.rootEl,r=e.cancel;this.isMultiDrag&&(Ue(!1,i),L.forEach(function(a){h(a,"display","")}),n(),te=!1,r())},hideClone:function(e){var n=this;e.sortable;var i=e.cloneNowHidden,r=e.cancel;this.isMultiDrag&&(L.forEach(function(a){h(a,"display","none"),n.options.removeCloneOnHide&&a.parentNode&&a.parentNode.removeChild(a)}),i(),te=!0,r())},dragStartGlobal:function(e){e.sortable,!this.isMultiDrag&&V&&V.multiDrag._deselectMultiDrag(),v.forEach(function(n){n.sortableIndex=N(n)}),v=v.sort(function(n,i){return n.sortableIndex-i.sortableIndex}),yt=!0},dragStarted:function(e){var n=this,i=e.sortable;if(this.isMultiDrag){if(this.options.sort&&(i.captureAnimationState(),this.options.animation)){v.forEach(function(a){a!==C&&h(a,"position","absolute")});var r=T(C,!1,!0,!0);v.forEach(function(a){a!==C&&Re(a,r)}),G=!0,Xt=!0}i.animateAll(function(){G=!1,Xt=!1,n.options.animation&&v.forEach(function(a){se(a)}),n.options.sort&&ee()})}},dragOver:function(e){var n=e.target,i=e.completed,r=e.cancel;G&&~v.indexOf(n)&&(i(!1),r())},revert:function(e){var n=e.fromSortable,i=e.rootEl,r=e.sortable,a=e.dragRect;v.length>1&&(v.forEach(function(l){r.addAnimationState({target:l,rect:G?T(l):a}),se(l),l.fromRect=a,n.removeAnimationState(l)}),G=!1,Pn(!this.options.removeCloneOnHide,i))},dragOverCompleted:function(e){var n=e.sortable,i=e.isOwner,r=e.insertion,a=e.activeSortable,l=e.parentEl,s=e.putSortable,u=this.options;if(r){if(i&&a._hideClone(),Xt=!1,u.animation&&v.length>1&&(G||!i&&!a.options.sort&&!s)){var d=T(C,!1,!0,!0);v.forEach(function(p){p!==C&&(Re(p,d),l.appendChild(p))}),G=!0}if(!i)if(G||ee(),v.length>1){var f=te;a._showClone(n),a.options.animation&&!te&&f&&L.forEach(function(p){a.addAnimationState({target:p,rect:Yt}),p.fromRect=Yt,p.thisAnimationDuration=null})}else a._showClone(n)}},dragOverAnimationCapture:function(e){var n=e.dragRect,i=e.isOwner,r=e.activeSortable;if(v.forEach(function(l){l.thisAnimationDuration=null}),r.options.animation&&!i&&r.multiDrag.isMultiDrag){Yt=U({},n);var a=ct(C,!0);Yt.top-=a.f,Yt.left-=a.e}},dragOverAnimationComplete:function(){G&&(G=!1,ee())},drop:function(e){var n=e.originalEvent,i=e.rootEl,r=e.parentEl,a=e.sortable,l=e.dispatchSortableEvent,s=e.oldIndex,u=e.putSortable,d=u||this.sortable;if(n){var f=this.options,p=r.children;if(!yt)if(f.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),A(C,f.selectedClass,!~v.indexOf(C)),~v.indexOf(C))v.splice(v.indexOf(C),1),kt=null,It({sortable:a,rootEl:i,name:"deselect",targetEl:C,originalEvent:n});else{if(v.push(C),It({sortable:a,rootEl:i,name:"select",targetEl:C,originalEvent:n}),n.shiftKey&&kt&&a.el.contains(kt)){var D=N(kt),w=N(C);~D&&~w&&D!==w&&(function(){var E,_;w>D?(_=D,E=w):(_=w,E=D+1);for(var M=f.filter;_<E;_++)if(!~v.indexOf(p[_])&&B(p[_],f.draggable,r,!1)){var J=M&&(typeof M=="function"?M.call(a,n,p[_],a):M.split(",").some(function(z){return B(p[_],z.trim(),r,!1)}));J||(A(p[_],f.selectedClass,!0),v.push(p[_]),It({sortable:a,rootEl:i,name:"select",targetEl:p[_],originalEvent:n}))}})()}else kt=C;V=d}if(yt&&this.isMultiDrag){if(G=!1,(r[F].options.sort||r!==i)&&v.length>1){var S=T(C),X=N(C,":not(."+this.options.selectedClass+")");if(!Xt&&f.animation&&(C.thisAnimationDuration=null),d.captureAnimationState(),!Xt&&(f.animation&&(C.fromRect=S,v.forEach(function(E){if(E.thisAnimationDuration=null,E!==C){var _=G?T(E):S;E.fromRect=_,d.addAnimationState({target:E,rect:_})}})),ee(),v.forEach(function(E){p[X]?r.insertBefore(E,p[X]):r.appendChild(E),X++}),s===N(C))){var W=!1;v.forEach(function(E){if(E.sortableIndex!==N(E)){W=!0;return}}),W&&(l("update"),l("sort"))}v.forEach(function(E){se(E)}),d.animateAll()}V=d}(i===r||u&&u.lastPutMode!=="clone")&&L.forEach(function(E){E.parentNode&&E.parentNode.removeChild(E)})}},nullingGlobal:function(){this.isMultiDrag=yt=!1,L.length=0},destroyGlobal:function(){this._deselectMultiDrag(),b(document,"pointerup",this._deselectMultiDrag),b(document,"mouseup",this._deselectMultiDrag),b(document,"touchend",this._deselectMultiDrag),b(document,"keydown",this._checkKeyDown),b(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(e){if(!(typeof yt<"u"&&yt)&&V===this.sortable&&!(e&&B(e.target,this.options.draggable,this.sortable.el,!1))&&!(e&&e.button!==0))for(;v.length;){var n=v[0];A(n,this.options.selectedClass,!1),v.shift(),It({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:n,originalEvent:e})}},_checkKeyDown:function(e){e.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(e){e.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},U(o,{pluginName:"multiDrag",utils:{select:function(e){var n=e.parentNode[F];!n||!n.options.multiDrag||~v.indexOf(e)||(V&&V!==n&&(V.multiDrag._deselectMultiDrag(),V=n),A(e,n.options.selectedClass,!0),v.push(e))},deselect:function(e){var n=e.parentNode[F],i=v.indexOf(e);!n||!n.options.multiDrag||!~i||(A(e,n.options.selectedClass,!1),v.splice(i,1))}},eventProperties:function(){var e=this,n=[],i=[];return v.forEach(function(r){n.push({multiDragElement:r,index:r.sortableIndex});var a;G&&r!==C?a=-1:G?a=N(r,":not(."+e.options.selectedClass+")"):a=N(r),i.push({multiDragElement:r,index:a})}),{items:nn(v),clones:[].concat(L),oldIndicies:n,newIndicies:i}},optionListeners:{multiDragKey:function(e){return e=e.toLowerCase(),e==="ctrl"?e="Control":e.length>1&&(e=e.charAt(0).toUpperCase()+e.substr(1)),e}}})}function Pn(o,t){v.forEach(function(e,n){var i=t.children[e.sortableIndex+(o?Number(n):0)];i?t.insertBefore(e,i):t.appendChild(e)})}function Ue(o,t){L.forEach(function(e,n){var i=t.children[e.sortableIndex+(o?Number(n):0)];i?t.insertBefore(e,i):t.appendChild(e)})}function ee(){v.forEach(function(o){o!==C&&o.parentNode&&o.parentNode.removeChild(o)})}g.mount(new On),g.mount(De,Ee);export{Nn as MultiDrag,g as Sortable,An as Swap,g as default};
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeadmin_configurable_columns
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kyryl Oliinyk
|
|
@@ -70,12 +70,16 @@ files:
|
|
|
70
70
|
- CHANGELOG.md
|
|
71
71
|
- LICENSE.txt
|
|
72
72
|
- README.md
|
|
73
|
+
- app/javascript/activeadmin_configurable_columns/picker.js
|
|
73
74
|
- app/models/activeadmin_configurable_columns/preference.rb
|
|
74
75
|
- app/views/activeadmin_configurable_columns/_form.html.erb
|
|
75
76
|
- app/views/activeadmin_configurable_columns/_page.html.erb
|
|
77
|
+
- app/views/activeadmin_configurable_columns/_script.html.erb
|
|
76
78
|
- app/views/activeadmin_configurable_columns/_sidebar.html.erb
|
|
79
|
+
- config/importmap.rb
|
|
77
80
|
- config/locales/en.yml
|
|
78
81
|
- db/migrate/20260930000001_create_activeadmin_configurable_columns_preferences.rb
|
|
82
|
+
- db/migrate/20261026000001_add_column_order_to_activeadmin_configurable_columns_preferences.rb
|
|
79
83
|
- lib/activeadmin_configurable_columns.rb
|
|
80
84
|
- lib/activeadmin_configurable_columns/admin/table_columns.rb
|
|
81
85
|
- lib/activeadmin_configurable_columns/column.rb
|
|
@@ -83,8 +87,11 @@ files:
|
|
|
83
87
|
- lib/activeadmin_configurable_columns/engine.rb
|
|
84
88
|
- lib/activeadmin_configurable_columns/registry.rb
|
|
85
89
|
- lib/activeadmin_configurable_columns/resource_extension.rb
|
|
90
|
+
- lib/activeadmin_configurable_columns/saved_selection.rb
|
|
91
|
+
- lib/activeadmin_configurable_columns/selection.rb
|
|
86
92
|
- lib/activeadmin_configurable_columns/table_renderer.rb
|
|
87
93
|
- lib/activeadmin_configurable_columns/version.rb
|
|
94
|
+
- vendor/javascript/activeadmin_configurable_columns/sortable.js
|
|
88
95
|
homepage: https://github.com/kyryloliinyk/activeadmin_configurable_columns
|
|
89
96
|
licenses:
|
|
90
97
|
- MIT
|