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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdf4162f0335b3294ed14cc99cc72ed22ce80e798276f9903e3f6a55fe2858f6
4
- data.tar.gz: 6082979b4e9332a4f3d904b6ae1838855515d4a26756ba6dc2f54edd0cb06720
3
+ metadata.gz: 63e61576e8abe89e94e69f4ca00f8ce7392f07c5faed46ba4fbe2653c27ca168
4
+ data.tar.gz: 1317c5848157b220c8e5d26ad89dc044513a892a3eb0ab27dfe1b13337713ea8
5
5
  SHA512:
6
- metadata.gz: 33db6eb5c5af30b9fc6e092942549b791fbf48f9e691daa3ee9d273f30b0af3bb41f55137429a626465497cf83a35f5bfadb8683e47fc86c837e80a14c66b33b
7
- data.tar.gz: 02a8485aa3eaac9ea24037f261ec15e82b519f216b5de39e61057fdbb7582face1cc01cb18ad7fd89854d176e2440f2fb85f7f8abc07423f3de32f2f5d5e1959
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 [![CI](https://github.com/kyryloliinyk/activeadmin_configurable_columns/actions/workflows/ci.yml/badge.svg)](https://github.com/kyryloliinyk/activeadmin_configurable_columns/actions/workflows/ci.yml)
2
+
2
3
 
3
- [![CI](https://github.com/kyryloliinyk/activeadmin_configurable_columns/actions/workflows/ci.yml/badge.svg)](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 ActiveAdmin's own extension
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
- # visible_column_keys(admin, resource_key) -> [Symbol] | nil (nil = never saved)
98
- # store(admin:, resource_key:, keys:)
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` looks up the current admin's saved keys and emits the matching columns.
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. The available columns themselves are declared in
4
- # the ActiveAdmin registration with the `configurable_columns` DSL.
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 validation below, not by the coder, so a bad assignment
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 keys this admin picked for the table, or nil when they never
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.visible_column_keys(admin, resource_key)
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)&.visible_columns&.map(&:to_sym)
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:, keys:)
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: keys.map(&:to_s))
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
- <%# Checkbox picker for one index table. Rendered both in the index sidebar and
2
- on the "Table columns" page, so it always posts back to the same action. %>
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
- <% selected = ActiveadminConfigurableColumns.visible_columns(registry, current_active_admin_user).map(&:key) %>
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
- <div class="flex flex-col gap-2 mb-4">
13
- <% registry.columns.each do |column| %>
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
- <div class="flex items-center gap-2">
16
- <%= check_box_tag 'visible_columns[]', column.key, selected.include?(column.key), id: checkbox_id %>
17
- <%= label_tag checkbox_id, column.human_label(resource.resource_class), class: 'text-sm' %>
18
- </div>
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
- </div>
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 %>
@@ -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
@@ -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"
@@ -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" sidebar of every
3
- # configurable index, and both post back to the page's update action.
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
- else
24
- registry = resource.column_registry
24
+ elsif params[:reset].present?
25
+ ActiveadminConfigurableColumns.reset_selection(resource.column_registry, current_active_admin_user)
25
26
 
26
- ActiveadminConfigurableColumns.preference_store.store(
27
- admin: current_active_admin_user,
28
- resource_key: registry.resource_key,
29
- keys: registry.selected_keys(params[:visible_columns])
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
- .visible_columns(registry, helpers.current_active_admin_user)
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
@@ -1,3 +1,3 @@
1
1
  module ActiveadminConfigurableColumns
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
@@ -35,8 +35,9 @@ module ActiveadminConfigurableColumns
35
35
 
36
36
  # Where saved selections live. Any object answering
37
37
  #
38
- # visible_column_keys(admin, resource_key) -> [Symbol] | nil (nil = never saved)
39
- # store(admin:, resource_key:, keys:) (persists a selection)
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 sees for this resource.
63
- def visible_columns(registry, admin)
64
- saved = preference_store.visible_column_keys(admin, registry.resource_key)
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
- registry.columns_for(saved || registry.default_keys)
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: 1.0.0
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