katalyst-navigation 1.2.0 → 1.3.1

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: 6424bb96df0ceb96b40aa6d225dbe12461784edb41c1b83863435a56db02fd68
4
- data.tar.gz: 6a46bbd339f8c171fe7a9791ad94281c6e0cb6f846f8bab04bd62edb8e8c339c
3
+ metadata.gz: faf8cc382bdaae60ca2a35d93dcdfe970e36d317912ed13038607e74cf68da45
4
+ data.tar.gz: '05539e1025ee15510fb6952a3bfec19534daa834af0dd80f303081badb3a68cf'
5
5
  SHA512:
6
- metadata.gz: 173290becb5111777b887baeefe3a93642d8b154cb6463fd0a84a83e0246be7159568cc836d3eb2ca4ca7b9d7a2b616eff69c284d7abbaa93f7f2a26e0f81f76
7
- data.tar.gz: cad3f1cf87f00b7868aac073b71d192345b341feb73a0d28de332fccd932278988c256cc9228d2f30eadc55b2de0c4070d8a263c323e235b22f18f087bbc9abb
6
+ metadata.gz: dd9484b91afa4c6e80347513fe063989b00f838b9b6ca7bd3ff448879f444a89a7789e9e3fe7216987f67ea23fa4ae036797841b6e8631be37b157b6000b807f
7
+ data.tar.gz: 650999ce4d2ead2f82c80ec97458edbc326c0c43c57c62a5bb72f86f1c79c7c8c0adee7dba0653b4ea09b05d135677ad4de582209b13d7d9a66592ca8b49a058
@@ -22,8 +22,7 @@
22
22
  }
23
23
  }
24
24
 
25
- [role="img"][value="link"],
26
- [role="img"][value="title"] {
25
+ [role="img"][title="Type"] {
27
26
  width: 1.5rem;
28
27
  height: 1.5rem;
29
28
  display: grid;
@@ -44,18 +43,18 @@
44
43
  line-height: 1.125rem;
45
44
  text-align: center;
46
45
  }
46
+ }
47
47
 
48
- &[value="link"] {
49
- &::before {
50
- content: "#";
51
- }
52
- }
48
+ [role="img"][value="heading"]::before {
49
+ content: "H";
50
+ }
53
51
 
54
- &[value="title"] {
55
- &::before {
56
- content: "T";
57
- }
58
- }
52
+ [role="img"][value="link"]::before {
53
+ content: "#";
54
+ }
55
+
56
+ [role="img"][value="button"]::before {
57
+ content: "#";
59
58
  }
60
59
 
61
60
  [role="img"][value="invisible"] {
@@ -28,6 +28,10 @@
28
28
  position: unset;
29
29
  }
30
30
 
31
+ &[data-item-type="heading"]:before {
32
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3Etext %7B font: 72px serif %7D%3C/style%3E%3Ctext x='7' y='56' textLength='48'%3EH%3C/text%3E%3C/svg%3E%0A");
33
+ }
34
+
31
35
  &[data-item-type="link"]:before {
32
36
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_67_1273)'%3E%3Cpath d='M16 22H32V26H16V22ZM40.2 24H44C44 18.48 39.52 14 34 14H26V17.8H34C37.42 17.8 40.2 20.58 40.2 24ZM7.8 24C7.8 20.58 10.58 17.8 14 17.8H22V14H14C8.48 14 4 18.48 4 24C4 29.52 8.48 34 14 34H22V30.2H14C10.58 30.2 7.8 27.42 7.8 24ZM38 24H34V30H28V34H34V40H38V34H44V30H38V24Z' /%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_67_1273'%3E%3Crect width='48' height='48' /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E");
33
37
  }
@@ -10,6 +10,10 @@ module Katalyst
10
10
  render locals: { item: @menu.items.build(type: new_item_params) }
11
11
  end
12
12
 
13
+ def edit
14
+ render locals: { item: @item }
15
+ end
16
+
13
17
  def create
14
18
  item = @menu.items.build(item_params)
15
19
  if item.save
@@ -19,10 +23,6 @@ module Katalyst
19
23
  end
20
24
  end
21
25
 
22
- def edit
23
- render locals: { item: @item }
24
- end
25
-
26
26
  def update
27
27
  @item.attributes = item_params
28
28
 
@@ -41,8 +41,17 @@ module Katalyst
41
41
  params[:type] || Link.name
42
42
  end
43
43
 
44
+ def item_params_type
45
+ type = params.require(:item).fetch(:type, "")
46
+ if Katalyst::Navigation.config.items.include?(type)
47
+ type.safe_constantize
48
+ else
49
+ Item
50
+ end
51
+ end
52
+
44
53
  def item_params
45
- params.require(:item).permit(%i[title url visible http_method new_tab type])
54
+ params.require(:item).permit(item_params_type.permitted_params)
46
55
  end
47
56
 
48
57
  def set_menu
@@ -9,10 +9,22 @@ module Katalyst
9
9
  render locals: { menus: menus, sort: sort }
10
10
  end
11
11
 
12
+ def show
13
+ menu = Menu.find(params[:id])
14
+
15
+ render locals: { menu: menu }
16
+ end
17
+
12
18
  def new
13
19
  render locals: { menu: Menu.new }
14
20
  end
15
21
 
22
+ def edit
23
+ menu = Menu.find(params[:id])
24
+
25
+ render locals: { menu: menu }
26
+ end
27
+
16
28
  def create
17
29
  @menu = Menu.new(menu_params)
18
30
 
@@ -23,18 +35,6 @@ module Katalyst
23
35
  end
24
36
  end
25
37
 
26
- def show
27
- menu = Menu.find(params[:id])
28
-
29
- render locals: { menu: menu }
30
- end
31
-
32
- def edit
33
- menu = Menu.find(params[:id])
34
-
35
- render locals: { menu: menu }
36
- end
37
-
38
38
  # PATCH /admins/navigation_menus/:slug
39
39
  def update
40
40
  menu = Menu.find(params[:id])
@@ -42,7 +42,8 @@ module Katalyst
42
42
  menu.attributes = menu_params
43
43
 
44
44
  unless menu.valid?
45
- return render :show, locals: { menu: menu }, status: :unprocessable_entity
45
+ return render turbo_stream: helpers.navigation_editor_errors(menu: menu),
46
+ status: :unprocessable_entity
46
47
  end
47
48
 
48
49
  case params[:commit]
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Navigation
5
+ module Editor
6
+ class Errors < Base
7
+ def build(**options)
8
+ turbo_frame_tag dom_id(menu, :errors) do
9
+ next unless menu.errors.any?
10
+
11
+ tag.div(class: "navigation-errors", **options) do
12
+ tag.h2("Errors in navigation") +
13
+ tag.ul(class: "errors") do
14
+ menu.errors.each do |error|
15
+ concat(tag.li(error.message))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -14,6 +14,7 @@ module Katalyst
14
14
  def build(options)
15
15
  form_with(model: menu, **default_options(id: menu_form_id, **options)) do |form|
16
16
  concat hidden_input
17
+ concat errors
17
18
  concat(capture { yield form })
18
19
  end
19
20
  end
@@ -26,6 +27,10 @@ module Katalyst
26
27
  tag.input(type: "hidden", name: "#{Item::ATTRIBUTES_SCOPE}[id]")
27
28
  end
28
29
 
30
+ def errors
31
+ Editor::Errors.new(self, menu).build
32
+ end
33
+
29
34
  def default_options(options)
30
35
  add_option(options, :data, :controller, MENU_CONTROLLER)
31
36
  add_option(options, :data, :action, ACTIONS)
@@ -4,11 +4,10 @@ module Katalyst
4
4
  module Navigation
5
5
  module EditorHelper
6
6
  def navigation_editor_new_items(menu)
7
- [
8
- Heading.new(menu: menu),
9
- Link.new(menu: menu),
10
- Button.new(menu: menu),
11
- ]
7
+ Katalyst::Navigation.config.items.map do |item_class|
8
+ item_class = item_class.is_a?(String) ? item_class.safe_constantize : item_class
9
+ item_class.new(menu: menu)
10
+ end
12
11
  end
13
12
 
14
13
  def navigation_editor_menu(menu:, **options, &block)
@@ -26,7 +25,13 @@ module Katalyst
26
25
  Editor::List.new(self, menu).items(item)
27
26
  end
28
27
 
29
- # Gene
28
+ # Generate a turbo stream fragment that will show structural errors to the user.
29
+ def navigation_editor_errors(menu:, **options)
30
+ turbo_stream.replace(dom_id(menu, :errors),
31
+ Editor::Errors.new(self, menu).build(**options))
32
+ end
33
+
34
+ # Generate a new item template.
30
35
  def navigation_editor_new_item(item:, menu: item.menu, **options, &block)
31
36
  Editor::NewItem.new(self, menu).build(item, **options, &block)
32
37
  end
@@ -1,22 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Rails/HelperInstanceVariable
3
4
  module Katalyst
4
5
  module Navigation
5
6
  module Frontend
6
7
  class Builder
7
- attr_accessor :template, :menu_options, :list_options, :item_options
8
+ attr_accessor :template
8
9
 
9
10
  delegate_missing_to :@template
10
11
 
11
12
  def initialize(template, list: {}, item: {}, **menu_options)
12
- self.template = template
13
- self.menu_options = menu_options
14
- self.list_options = list
15
- self.item_options = item
13
+ self.template = template
14
+ @menu_options = menu_options.freeze
15
+ @list_options = list.freeze
16
+ @item_options = item.freeze
16
17
  end
17
18
 
18
19
  def render(tree)
19
- tag.ul **menu_options do
20
+ tag.ul(**menu_options(tree)) do
20
21
  tree.each do |item|
21
22
  concat render_item(item)
22
23
  end
@@ -26,32 +27,47 @@ module Katalyst
26
27
  def render_item(item)
27
28
  return unless item.visible?
28
29
 
29
- tag.li **item_options do
30
+ tag.li(**item_options(item)) do
30
31
  concat public_send("render_#{item.model_name.param_key}", item)
31
- concat render_list(item.children) if item.children.any?
32
+ concat render_children(item) if item.children.any?
32
33
  end
33
34
  end
34
35
 
35
- def render_list(items)
36
- tag.ul **list_options do
37
- items.each do |child|
36
+ def render_children(item)
37
+ tag.ul(**list_options(item)) do
38
+ item.children.each do |child|
38
39
  concat render_item(child)
39
40
  end
40
41
  end
41
42
  end
42
43
 
43
44
  def render_heading(heading)
44
- tag.header { tag.p heading.title }
45
+ tag.span(heading.title)
45
46
  end
46
47
 
47
48
  def render_link(link)
48
- link_to(link.title, link.url, item_options)
49
+ link_to(link.title, link.url)
49
50
  end
50
51
 
51
52
  def render_button(link)
52
- link_to(link.title, link.url, **item_options, method: link.http_method)
53
+ link_to(link.title, link.url, method: link.http_method)
54
+ end
55
+
56
+ private
57
+
58
+ def menu_options(_tree)
59
+ @menu_options
60
+ end
61
+
62
+ def list_options(_item)
63
+ @list_options
64
+ end
65
+
66
+ def item_options(_item)
67
+ @item_options
53
68
  end
54
69
  end
55
70
  end
56
71
  end
57
72
  end
73
+ # rubocop:enable Rails/HelperInstanceVariable
@@ -10,6 +10,10 @@ module Katalyst
10
10
 
11
11
  validates :title, :url, :http_method, presence: true
12
12
  validates :http_method, inclusion: { in: HTTP_METHODS.values.map(&:to_s) }
13
+
14
+ def self.permitted_params
15
+ super + %i[http_method]
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -10,6 +10,16 @@ module Katalyst
10
10
 
11
11
  attr_accessor :parent, :children, :index, :depth
12
12
 
13
+ def self.permitted_params
14
+ %i[
15
+ title
16
+ url
17
+ visible
18
+ new_tab
19
+ type
20
+ ]
21
+ end
22
+
13
23
  def layout?
14
24
  is_a? Layout
15
25
  end
@@ -69,6 +69,11 @@ module Katalyst
69
69
  self
70
70
  end
71
71
 
72
+ # Required for testing items validation
73
+ def items_attributes
74
+ draft_version&.nodes&.as_json
75
+ end
76
+
72
77
  # Updates the current draft version with new structure. Attributes should be structural information about the
73
78
  # items, e.g. `{index => {id:, depth:}` or `[{id:, depth:}]`.
74
79
  #
@@ -105,6 +110,8 @@ module Katalyst
105
110
 
106
111
  attribute :nodes, Types::NodesType.new, default: -> { [] }
107
112
 
113
+ validate :ensure_items_exists
114
+
108
115
  def items
109
116
  # support building menus in memory
110
117
  # requires that items are added in order and index and depth are set
@@ -112,12 +119,18 @@ module Katalyst
112
119
 
113
120
  items = parent.items.where(id: nodes.map(&:id)).index_by(&:id)
114
121
  nodes.map do |node|
115
- item = items[node.id]
116
- item.index = node.index
117
- item.depth = node.depth
118
- item
122
+ items[node.id]&.tap do |item|
123
+ item.index = node.index
124
+ item.depth = node.depth
125
+ end
119
126
  end
120
127
  end
128
+
129
+ private
130
+
131
+ def ensure_items_exists
132
+ parent.errors.add(:items, :missing_item) unless items.all?(&:present?)
133
+ end
121
134
  end
122
135
  end
123
136
  end
@@ -2,7 +2,7 @@
2
2
  <div class="tree" data-invisible="<%= !item.visible? %>">
3
3
  <%= builder.accordion_actions %>
4
4
 
5
- <span role="img" value="<%= item.url.present? ? "link" : "title" %>" title="Type"></span>
5
+ <span role="img" value="<%= item.model_name.param_key %>" title="Type"></span>
6
6
  <h4 class="title" title="<%= item.title %>"><%= item.title %></h4>
7
7
  <span role="img" value="invisible" title="Hidden"></span>
8
8
  </div>
@@ -1,4 +1,8 @@
1
1
  en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ missing_item: Items are missing or invalid
2
6
  views:
3
7
  katalyst:
4
8
  navigation:
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/configurable"
4
+
5
+ module Katalyst
6
+ module Navigation
7
+ class Config
8
+ include ActiveSupport::Configurable
9
+
10
+ config_accessor(:items) do
11
+ %w[
12
+ Katalyst::Navigation::Heading
13
+ Katalyst::Navigation::Link
14
+ Katalyst::Navigation::Button
15
+ ]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Katalyst
4
4
  module Navigation
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.1"
6
6
  end
7
7
  end
@@ -1,9 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "katalyst/navigation/config"
3
4
  require "katalyst/navigation/engine"
4
5
  require "katalyst/navigation/version"
5
6
 
6
7
  module Katalyst
7
8
  module Navigation # :nodoc:
9
+ extend self
10
+
11
+ def config
12
+ @config ||= Config.new
13
+ end
14
+
15
+ def configure
16
+ yield config
17
+ end
8
18
  end
9
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-navigation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-25 00:00:00.000000000 Z
11
+ date: 2022-11-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -39,6 +39,7 @@ files:
39
39
  - app/controllers/katalyst/navigation/items_controller.rb
40
40
  - app/controllers/katalyst/navigation/menus_controller.rb
41
41
  - app/helpers/katalyst/navigation/editor/base.rb
42
+ - app/helpers/katalyst/navigation/editor/errors.rb
42
43
  - app/helpers/katalyst/navigation/editor/item.rb
43
44
  - app/helpers/katalyst/navigation/editor/list.rb
44
45
  - app/helpers/katalyst/navigation/editor/menu.rb
@@ -78,6 +79,7 @@ files:
78
79
  - db/migrate/20220826034507_create_katalyst_navigation_items.rb
79
80
  - db/migrate/20220908044500_add_depth_limit_to_menus.rb
80
81
  - lib/katalyst/navigation.rb
82
+ - lib/katalyst/navigation/config.rb
81
83
  - lib/katalyst/navigation/engine.rb
82
84
  - lib/katalyst/navigation/version.rb
83
85
  - lib/tasks/yarn.rake