keystone_ui-preferences 0.4.0 → 0.5.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/README.md +37 -0
- data/app/models/keystone_ui/preferences/navigation_arrangement.rb +29 -0
- data/app/models/keystone_ui/preferences/pick_navigation_order.rb +48 -0
- data/app/models/keystone_ui/preferences/pick_navigation_placement.rb +30 -0
- data/app/views/keystone_ui/preferences/settings/_navigation_order.html.erb +58 -0
- data/app/views/keystone_ui/preferences/settings/_navigation_placement.html.erb +15 -0
- data/lib/generators/keystone_ui/preferences/install/templates/create_keystone_ui_preferences_component_preferences.rb.erb +1 -1
- data/lib/generators/keystone_ui/preferences/update/templates/remove_extra_owner_index_from_keystone_ui_preferences_component_preferences.rb.erb +5 -0
- data/lib/generators/keystone_ui/preferences/update/update_generator.rb +25 -0
- data/lib/keystone_ui/preferences/version.rb +1 -1
- data/lib/keystone_ui/preferences.rb +1 -0
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 132478f987e0dbbf12eeda5ac9c9e9fe14a9ca1ec8b8ce60618b29e272f5db8f
|
|
4
|
+
data.tar.gz: eefd3cd44bce3d9aa5f3dab0c3ff4f5ad4a882e15ab9fbd836964e46e631724a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da15030c2de6a478df7106c48218dfe8bcd2197f592f68ea8147b5b6fa389b5c65b4e30699a1480df86f13ec1d8069a2da2058c38550e8ed539e74ccab8dfe74
|
|
7
|
+
data.tar.gz: be3c817d9c47d16df821adeb5cf7fbe2e6cb884ad48e3711e00f2d270619c25368a202d0364593fa15bea9f7eefc2651027a3f80fa225fd4fe4645b6c692d732
|
data/README.md
CHANGED
|
@@ -18,6 +18,13 @@ bin/rails generate keystone_ui:preferences:install
|
|
|
18
18
|
bin/rails db:migrate
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
After updating the gem, run its update generator to copy any migrations added since it was installed:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bin/rails generate keystone_ui:preferences:update
|
|
25
|
+
bin/rails db:migrate
|
|
26
|
+
```
|
|
27
|
+
|
|
21
28
|
Mount the engine in `config/routes.rb`. A table's Columns menu saves to it:
|
|
22
29
|
|
|
23
30
|
```ruby
|
|
@@ -94,6 +101,36 @@ end
|
|
|
94
101
|
|
|
95
102
|
Each key has the members-choose switch and, when the admin has a saved layout of their own, a button that makes it the account's layout.
|
|
96
103
|
|
|
104
|
+
## Navigation placement
|
|
105
|
+
|
|
106
|
+
A person chooses where keystone_ui draws the desktop navigation, at the top or as a sidebar on the left or the right, in a settings_hub section the app registers:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
SettingsHub.section :navigation_placement, area: :user, title: "Navigation",
|
|
110
|
+
renders: "keystone_ui/preferences/settings/navigation_placement",
|
|
111
|
+
runs: "KeystoneUi::Preferences::PickNavigationPlacement"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The section offers Top, Left and Right, with the placement that applies to the person selected, or Top when nothing is saved. Saving keeps `"placement": "left"` for that person under the `navigation` key, leaving any saved tab order as it was, and keystone_ui's navigation reads it on the next page they open. A placement other than `top`, `left` or `right` is refused with a message, and nothing is saved.
|
|
115
|
+
|
|
116
|
+
The `navigation` key follows the account layouts above, so an account that turns off members-choose for it draws its own placement and tab order for every member.
|
|
117
|
+
|
|
118
|
+
While it is off, the placement and tab order sections say the account sets the navigation for everyone and offer no save. A placement, tab order or reset sent anyway is refused with a message, and nothing is saved.
|
|
119
|
+
|
|
120
|
+
## Tab order
|
|
121
|
+
|
|
122
|
+
A person sets the order of the navigation's groups, and of the tabs in each group, in a settings_hub section the app registers:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
SettingsHub.section :navigation_order, area: :user, title: "Tab order",
|
|
126
|
+
renders: "keystone_ui/preferences/settings/navigation_order",
|
|
127
|
+
runs: "KeystoneUi::Preferences::PickNavigationOrder"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The section lists the groups declared with keystone_ui's `navigation_group`, each with the tabs the person may see, in the order that applies to them. A group with no tab they may see is left out. Each group and tab has an Up and a Down button, and pressing one saves the order with it moved one place, so the page works in a Hotwire Native web view with no script and nothing to drag. At phone width each row's buttons sit under its name.
|
|
131
|
+
|
|
132
|
+
Saving keeps `"order": [{ "group": "Admin" }, { "group": "Sales", "tabs": ["orders", "quotes"] }]` for that person under the `navigation` key, naming groups by label and tabs by key, and leaves their saved placement as it was. keystone_ui draws the navigation in that order on the next page they open, with anything the order does not name after it in its declared order. Reset removes the saved order, so the navigation goes back to the declared order. An order that is not a list of groups is refused with a message, and nothing is saved.
|
|
133
|
+
|
|
97
134
|
## What is stored
|
|
98
135
|
|
|
99
136
|
One row per owner and component key, in `keystone_ui_preferences_component_preferences`:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
module Preferences
|
|
5
|
+
class NavigationArrangement
|
|
6
|
+
def initialize(arranged)
|
|
7
|
+
@arranged = arranged
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def group_moved(index, step)
|
|
11
|
+
moved(order, index, step)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def tab_moved(group_index, tab_index, step)
|
|
15
|
+
order.tap { |groups| moved(groups[group_index]["tabs"], tab_index, step) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def order
|
|
19
|
+
@arranged.map { |group, tabs| { "group" => group.label, "tabs" => tabs.map { |tab| tab.key.to_s } } }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def moved(items, index, step)
|
|
25
|
+
items.insert(index + step, items.delete_at(index))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module KeystoneUi
|
|
6
|
+
module Preferences
|
|
7
|
+
class PickNavigationOrder
|
|
8
|
+
def initialize(values:, person: nil, account: nil)
|
|
9
|
+
@person = person
|
|
10
|
+
@account = account
|
|
11
|
+
@values = values
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
return Refusal.new("Your account sets the navigation for everyone, so this was not saved.") unless members_choose?
|
|
16
|
+
return reset if @values[:reset] == "1"
|
|
17
|
+
return Refusal.new("That navigation order is too large to save.") if @values[:order].to_s.bytesize > KeystoneUi::Preferences.configuration.max_value_bytes
|
|
18
|
+
|
|
19
|
+
order = submitted_order
|
|
20
|
+
return Refusal.new("Choose an order for the navigation's groups and tabs.") unless order.is_a?(Array)
|
|
21
|
+
|
|
22
|
+
preference.update!(value: preference.value.to_h.merge("order" => order))
|
|
23
|
+
Kept.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def members_choose?
|
|
29
|
+
LayoutChoice.new(person: @person, account: @account, component_key: "navigation").members_choose?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reset
|
|
33
|
+
preference.update!(value: preference.value.to_h.except("order"))
|
|
34
|
+
Kept.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def preference
|
|
38
|
+
@preference ||= ComponentPreference.find_or_initialize_by(owner: @person, component_key: "navigation")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def submitted_order
|
|
42
|
+
JSON.parse(@values[:order].to_s)
|
|
43
|
+
rescue JSON::ParserError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KeystoneUi
|
|
4
|
+
module Preferences
|
|
5
|
+
class PickNavigationPlacement
|
|
6
|
+
PLACEMENTS = %w[top left right].freeze
|
|
7
|
+
|
|
8
|
+
def initialize(values:, person: nil, account: nil)
|
|
9
|
+
@person = person
|
|
10
|
+
@account = account
|
|
11
|
+
@values = values
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
return Refusal.new("Your account sets the navigation for everyone, so this was not saved.") unless members_choose?
|
|
16
|
+
return Refusal.new("Choose Top, Left or Right for the navigation.") unless PLACEMENTS.include?(@values[:placement])
|
|
17
|
+
|
|
18
|
+
preference = ComponentPreference.find_or_initialize_by(owner: @person, component_key: "navigation")
|
|
19
|
+
preference.update!(value: preference.value.to_h.merge("placement" => @values[:placement]))
|
|
20
|
+
Kept.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def members_choose?
|
|
26
|
+
LayoutChoice.new(person: @person, account: @account, component_key: "navigation").members_choose?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<% choice = KeystoneUi::Preferences::LayoutChoice.new(person: person, account: account, component_key: "navigation") %>
|
|
2
|
+
<% saved_order = choice.value&.dig("order") || [] %>
|
|
3
|
+
|
|
4
|
+
<% if choice.members_choose? %>
|
|
5
|
+
<% permitted = KeystoneUi.configuration.navigation_groups.map { |group| [ group, group.tabs_permitted_for(self) ] }.reject { |_group, tabs| tabs.empty? } %>
|
|
6
|
+
<% arranged = KeystoneUi::NavigationOrder.new(saved_order).arrange(permitted) %>
|
|
7
|
+
<% arrangement = KeystoneUi::Preferences::NavigationArrangement.new(arranged) %>
|
|
8
|
+
|
|
9
|
+
<% arranged.each_with_index do |(group, tabs), group_index| %>
|
|
10
|
+
<div data-navigation-group="<%= group.label %>">
|
|
11
|
+
<%= ui_grid(cols: { default: 1, sm: 2 }, gap: :sm) do %>
|
|
12
|
+
<p data-group-label><strong><%= group.label %></strong></p>
|
|
13
|
+
<%= ui_grid(cols: { default: 2 }, gap: :sm) do %>
|
|
14
|
+
<% if group_index.positive? %>
|
|
15
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false, group_move: "up" }) do %>
|
|
16
|
+
<%= hidden_field_tag "order", arrangement.group_moved(group_index, -1).to_json, id: nil %>
|
|
17
|
+
<%= ui_button(label: "Up", type: :submit, variant: :secondary, size: :sm) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% if group_index < arranged.size - 1 %>
|
|
21
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false, group_move: "down" }) do %>
|
|
22
|
+
<%= hidden_field_tag "order", arrangement.group_moved(group_index, 1).to_json, id: nil %>
|
|
23
|
+
<%= ui_button(label: "Down", type: :submit, variant: :secondary, size: :sm) %>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
28
|
+
<% tabs.each_with_index do |tab, tab_index| %>
|
|
29
|
+
<div data-navigation-tab="<%= tab.key %>">
|
|
30
|
+
<%= ui_grid(cols: { default: 1, sm: 2 }, gap: :sm) do %>
|
|
31
|
+
<span data-tab-label><%= tab.label %></span>
|
|
32
|
+
<%= ui_grid(cols: { default: 2 }, gap: :sm) do %>
|
|
33
|
+
<% if tab_index.positive? %>
|
|
34
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false, tab_move: "up" }) do %>
|
|
35
|
+
<%= hidden_field_tag "order", arrangement.tab_moved(group_index, tab_index, -1).to_json, id: nil %>
|
|
36
|
+
<%= ui_button(label: "Up", type: :submit, variant: :secondary, size: :sm) %>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% if tab_index < tabs.size - 1 %>
|
|
40
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false, tab_move: "down" }) do %>
|
|
41
|
+
<%= hidden_field_tag "order", arrangement.tab_moved(group_index, tab_index, 1).to_json, id: nil %>
|
|
42
|
+
<%= ui_button(label: "Down", type: :submit, variant: :secondary, size: :sm) %>
|
|
43
|
+
<% end %>
|
|
44
|
+
<% end %>
|
|
45
|
+
<% end %>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
<% end %>
|
|
49
|
+
</div>
|
|
50
|
+
<% end %>
|
|
51
|
+
|
|
52
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false }) do %>
|
|
53
|
+
<%= hidden_field_tag "reset", "1", id: nil %>
|
|
54
|
+
<%= ui_button(label: "Reset", type: :submit, variant: :secondary) %>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% else %>
|
|
57
|
+
<%= ui_alert(message: "Your account sets the navigation for everyone.") %>
|
|
58
|
+
<% end %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% choice = KeystoneUi::Preferences::LayoutChoice.new(person: person, account: account, component_key: "navigation") %>
|
|
2
|
+
<% current = choice.value&.dig("placement") || "top" %>
|
|
3
|
+
|
|
4
|
+
<% if choice.members_choose? %>
|
|
5
|
+
<%= form_with(url: submit_url, method: :patch, data: { turbo: false }) do %>
|
|
6
|
+
<%= ui_grid(cols: { default: 1, sm: 3 }, gap: :sm) do %>
|
|
7
|
+
<% { "top" => "Top", "left" => "Left", "right" => "Right" }.each do |placement, label| %>
|
|
8
|
+
<%= ui_radio_card(name: "placement", value: placement, label: label, checked: placement == current) %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<%= ui_button(label: "Save", type: :submit) %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% else %>
|
|
14
|
+
<%= ui_alert(message: "Your account sets the navigation for everyone.") %>
|
|
15
|
+
<% end %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class CreateKeystoneUiPreferencesComponentPreferences < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
2
|
def change
|
|
3
3
|
create_table :keystone_ui_preferences_component_preferences do |t|
|
|
4
|
-
t.references :owner, polymorphic: true, null: false
|
|
4
|
+
t.references :owner, polymorphic: true, null: false, index: false
|
|
5
5
|
t.string :component_key, null: false
|
|
6
6
|
t.json :value, null: false, default: {}
|
|
7
7
|
t.boolean :members_choose, default: true, null: false
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
class RemoveExtraOwnerIndexFromKeystoneUiPreferencesComponentPreferences < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
remove_index :keystone_ui_preferences_component_preferences, %i[owner_type owner_id], name: "index_keystone_ui_preferences_component_preferences_on_owner", if_exists: true
|
|
4
|
+
end
|
|
5
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module KeystoneUi
|
|
7
|
+
module Preferences
|
|
8
|
+
module Generators
|
|
9
|
+
class UpdateGenerator < Rails::Generators::Base
|
|
10
|
+
include ActiveRecord::Generators::Migration
|
|
11
|
+
|
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
|
13
|
+
|
|
14
|
+
desc "Updates KeystoneUi::Preferences: copies migrations added since it was installed."
|
|
15
|
+
|
|
16
|
+
def copy_remove_extra_owner_index_migration
|
|
17
|
+
migration_template(
|
|
18
|
+
"remove_extra_owner_index_from_keystone_ui_preferences_component_preferences.rb.erb",
|
|
19
|
+
"db/migrate/remove_extra_owner_index_from_keystone_ui_preferences_component_preferences.rb"
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: keystone_ui-preferences
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler Schneider
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.38.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.38.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: railties
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -83,12 +83,19 @@ files:
|
|
|
83
83
|
- app/models/keystone_ui/preferences/component_preference.rb
|
|
84
84
|
- app/models/keystone_ui/preferences/kept.rb
|
|
85
85
|
- app/models/keystone_ui/preferences/layout_choice.rb
|
|
86
|
+
- app/models/keystone_ui/preferences/navigation_arrangement.rb
|
|
86
87
|
- app/models/keystone_ui/preferences/pick_account_layouts.rb
|
|
88
|
+
- app/models/keystone_ui/preferences/pick_navigation_order.rb
|
|
89
|
+
- app/models/keystone_ui/preferences/pick_navigation_placement.rb
|
|
87
90
|
- app/models/keystone_ui/preferences/refusal.rb
|
|
88
91
|
- app/views/keystone_ui/preferences/settings/_account_layouts.html.erb
|
|
92
|
+
- app/views/keystone_ui/preferences/settings/_navigation_order.html.erb
|
|
93
|
+
- app/views/keystone_ui/preferences/settings/_navigation_placement.html.erb
|
|
89
94
|
- config/routes.rb
|
|
90
95
|
- lib/generators/keystone_ui/preferences/install/install_generator.rb
|
|
91
96
|
- lib/generators/keystone_ui/preferences/install/templates/create_keystone_ui_preferences_component_preferences.rb.erb
|
|
97
|
+
- lib/generators/keystone_ui/preferences/update/templates/remove_extra_owner_index_from_keystone_ui_preferences_component_preferences.rb.erb
|
|
98
|
+
- lib/generators/keystone_ui/preferences/update/update_generator.rb
|
|
92
99
|
- lib/keystone_ui-preferences.rb
|
|
93
100
|
- lib/keystone_ui/preferences.rb
|
|
94
101
|
- lib/keystone_ui/preferences/configuration.rb
|