fox_tail 0.1.0 → 0.1.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: 6fa160d3775e0297eee79f1c0378e6388ba468d90993e4c07f72ce6b2844088b
4
- data.tar.gz: 497c59c7a8198c90231b5ca3dc37e651f57dd41d13fb89aa64dac9d5e6ffa4c3
3
+ metadata.gz: f20728d2c996fb0a45ee4b0a84077dceb7655cf55574b9c202a365c56458b21f
4
+ data.tar.gz: 5fa861761ba7666416ffcf68d285fdab1e8c6ca07df018464347aa9baff6540c
5
5
  SHA512:
6
- metadata.gz: 20ca963b2ad4fbea94eac861ae42ff5e40705f11f804a6554db25d59f8d589e986299901aaafcabeed044d8c7a1db7b4330ea768e406f9fe231442bfcbed5794
7
- data.tar.gz: f9383331cdc86d121d1edc3fc4ade66b040a84cae6a243d94d9f7b5a1ab4b88281b01aa3f8212306f2e02486f5ac915acde9733108bfee92dde9b956c1aef388
6
+ metadata.gz: 43ceccf17c6a3a9acb833c4862238c24bb3c1c517bee8865d272c4dff4fe22a6917aa75dcea0f55a559e402c0826a9ce63badc5593ff5cc146b474876d61e626
7
+ data.tar.gz: 469e328834ba309334daceae9640898cd8077909b7da314a3e62c9887f79dfe47113ed0a51bd47289bc8d444b8acbb61a91c3f4e54c3164386b6cae632651bba
@@ -6,9 +6,15 @@ class FoxTail::AlertComponent < FoxTail::DismissibleComponent
6
6
 
7
7
  attr_reader :id
8
8
 
9
- renders_one :header, lambda { |options = {}, &block|
9
+ renders_one :header, lambda { |text_or_options = {}, options = {}, &block|
10
+ if block
11
+ options = text_or_options
12
+ text_or_options = nil
13
+ end
14
+
15
+ text_or_options ||= capture(&block)
10
16
  options[:class] = classnames theme.apply(:header, self), options[:class]
11
- content_tag(:h3, options, &block)
17
+ content_tag :h3, text_or_options, options
12
18
  }
13
19
 
14
20
  renders_one :icon, lambda { |options = {}|
@@ -24,6 +30,7 @@ class FoxTail::AlertComponent < FoxTail::DismissibleComponent
24
30
  icon_options[:class] = theme.apply "dismiss.icon", self
25
31
  dismiss_actions! options
26
32
  options[:class] = classnames theme.apply("dismiss.button", self), options[:class]
33
+ options[:type] = :button
27
34
 
28
35
  content_tag :button, options do
29
36
  concat render(FoxTail::IconBaseComponent.new(icon_options[:icon], icon_options.except(:icon)))
@@ -3,6 +3,28 @@
3
3
  class FoxTail::ButtonBaseComponent < FoxTail::ClickableComponent
4
4
  include FoxTail::Concerns::Formable
5
5
 
6
+ renders_one :indicator, types: {
7
+ dot: {
8
+ as: :dot_indicator,
9
+ renders: lambda { |options = {}|
10
+ styling_options = options.extract!(:position).reverse_merge(position: :top_right)
11
+ options[:size] ||= :sm
12
+ options[:class] = classnames theme.apply(:indicator, self, styling_options), options[:class]
13
+ FoxTail::DotIndicatorComponent.new options
14
+ }
15
+ },
16
+ badge: {
17
+ as: :badge_indicator,
18
+ renders: lambda { |options = {}|
19
+ styling_options = options.extract!(:position).reverse_merge(position: :top_right)
20
+ options[:size] ||= :sm
21
+ options[:pill] = true unless options.key?(:pill)
22
+ options[:class] = classnames theme.apply(:indicator, self, styling_options), options[:class]
23
+ FoxTail::BadgeComponent.new options
24
+ }
25
+ }
26
+ }
27
+
6
28
  has_option :variant, default: :solid
7
29
  has_option :size, default: :base
8
30
  has_option :color, default: :default
@@ -106,3 +106,14 @@ root:
106
106
  default: *default
107
107
  root/disabled:
108
108
  base: pointer-events-none cursor-not-allowed opacity-50
109
+ indicator:
110
+ base: absolute
111
+ position:
112
+ top_left: top-0 left-0 -translate-x-1/2 -translate-y-1/2
113
+ top: top-0 left-1/2 -translate-x-1/2 -translate-y-1/2
114
+ top_right: top-0 right-0 translate-x-1/2 -translate-y-1/2
115
+ center_left: top-1/2 left-0 -translate-x-1/2 -translate-y-1/2
116
+ center_right: top-1/2 right-0 translate-x-1/2 -translate-y-1/2
117
+ bottom_left: bottom-0 left-0 -translate-x-1/2 translate-y-1/2
118
+ bottom: bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2
119
+ bottom_right: bottom-0 right-0 translate-x-1/2 translate-y-1/2
@@ -55,6 +55,7 @@ class FoxTail::ButtonComponent < FoxTail::ButtonBaseComponent
55
55
 
56
56
  def call
57
57
  super do
58
+ concat indicator if indicator?
58
59
  concat loading_icon if loading_icon?
59
60
  concat left_visual if left_visual?
60
61
 
@@ -11,12 +11,6 @@ module FoxTail::Concerns::Placeholderable
11
11
  !!placeholder.presence
12
12
  end
13
13
 
14
- def before_render
15
- super
16
-
17
- html_attributes[:placeholder] = retrieve_placeholder if placeholder?
18
- end
19
-
20
14
  protected
21
15
 
22
16
  def retrieve_placeholder
@@ -28,7 +28,7 @@ export default class extends Controller {
28
28
  declare readonly dismissedClasses: string[];
29
29
 
30
30
  private _dismissed: boolean = false;
31
- private _autoCloseTimer: number | null = null;
31
+ private _autoCloseTimer: NodeJS.Timeout | null = null;
32
32
 
33
33
  connect() {
34
34
  super.connect();
@@ -60,6 +60,7 @@ class FoxTail::DrawerComponent < FoxTail::BaseComponent
60
60
  content_tag tag_name, html_attributes do
61
61
  concat close_button if close_button?
62
62
  concat notch if notch?
63
+ yield if block_given?
63
64
  concat content
64
65
  end
65
66
  end
@@ -20,6 +20,7 @@ class FoxTail::IconButtonComponent < FoxTail::ButtonBaseComponent
20
20
  def call
21
21
  super do
22
22
  icons.each { |icon| concat icon }
23
+ concat indicator if indicator?
23
24
 
24
25
  if content? || i18n_content.present?
25
26
  concat content_tag(:span, retrieve_content, class: theme.apply(:content, self))
@@ -7,18 +7,23 @@ class FoxTail::InlineSvgComponent < FoxTail::BaseComponent
7
7
 
8
8
  has_option :raise, as: :raise_error, type: :boolean
9
9
 
10
- def initialize(path, html_attributes = {})
10
+ def initialize(path_or_html_attributes = {}, html_attributes = {})
11
+ if path_or_html_attributes.is_a?(Hash)
12
+ html_attributes = path_or_html_attributes
13
+ path_or_html_attributes = nil
14
+ end
15
+
11
16
  unless html_attributes.key? :raise
12
17
  html_attributes[:raise] = FoxTail::Base.fox_tail_config.raise_on_asset_not_found
13
18
  end
14
19
 
15
20
  super(html_attributes)
16
21
 
17
- @path = path.to_s
22
+ @path = path_or_html_attributes&.to_s
18
23
  end
19
24
 
20
25
  def call
21
- doc = Nokogiri::XML.parse asset_contents
26
+ doc = Nokogiri::XML.parse content? ? content : asset_contents
22
27
  sanitized_html_attributes.each { |k, v| doc.child[k.to_s] = v }
23
28
  doc.child[:class] = html_class
24
29
  yield doc if block_given?
@@ -17,6 +17,7 @@ class FoxTail::InputBaseComponent < FoxTail::BaseComponent
17
17
  super
18
18
 
19
19
  add_default_name_and_id
20
+ html_attributes[:placeholder] = retrieve_placeholder if placeholder?
20
21
  end
21
22
 
22
23
  protected
@@ -4,7 +4,6 @@ class FoxTail::LinkComponent < FoxTail::ClickableComponent
4
4
  has_option :color, default: :default
5
5
 
6
6
  def initialize(url, html_attributes = {})
7
- super(html_attributes)
8
- @url = url
7
+ super(html_attributes.merge(url: url))
9
8
  end
10
9
  end
@@ -4,7 +4,7 @@ class FoxTail::ModalComponent < FoxTail::BaseComponent
4
4
  include FoxTail::Concerns::HasStimulusController
5
5
 
6
6
  renders_one :trigger, lambda { |options = {}|
7
- FoxTail::ModalTriggerComponent.new options.delete(:id), "##{tag_id}", options
7
+ self.class.trigger_component.new options.delete(:id), "##{tag_id}", options
8
8
  }
9
9
 
10
10
  has_option :placement, default: :center
@@ -115,6 +115,10 @@ class FoxTail::ModalComponent < FoxTail::BaseComponent
115
115
  def stimulus_controller_name
116
116
  :modal
117
117
  end
118
+
119
+ def trigger_component
120
+ FoxTail::ModalTriggerComponent
121
+ end
118
122
  end
119
123
 
120
124
  class StimulusController < FoxTail::StimulusController
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class FoxTail::SelectComponent < FoxTail::InputBaseComponent
4
+ include FoxTail::Concerns::Placeholderable
5
+
4
6
  has_option :disabled, type: :boolean, default: false
5
7
  has_option :value
6
8
  has_option :include_hidden, type: :boolean, default: true
9
+ has_option :include_blank, type: :boolean, default: false
7
10
 
8
- renders_one :placeholder, lambda { |text, selected: false, disabled: true|
11
+ renders_one :prompt, lambda { |text, selected: false, disabled: true|
9
12
  FoxTail::Select::OptionComponent.new("", disabled: disabled, selected: selected).with_content(text)
10
13
  }
11
14
 
@@ -35,6 +38,12 @@ class FoxTail::SelectComponent < FoxTail::InputBaseComponent
35
38
 
36
39
  html_attributes[:class] = classnames theme.apply(:root, self), html_class
37
40
  html_attributes[:multiple] = :multiple if html_attributes[:multiple]
41
+
42
+ if placeholder?
43
+ with_prompt retrieve_placeholder, selected: value_from_object.nil?
44
+ elsif include_blank?
45
+ with_prompt "", disabled: false, selected: value_from_object.nil?
46
+ end
38
47
  end
39
48
 
40
49
  def call
@@ -62,7 +71,7 @@ class FoxTail::SelectComponent < FoxTail::InputBaseComponent
62
71
 
63
72
  def render_select
64
73
  content_tag :select, html_attributes do
65
- concat placeholder if placeholder?
74
+ concat prompt if prompt?
66
75
  select_options.each { |select_option| concat select_option } if select_options?
67
76
  concat content if content?
68
77
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FoxTail::Sidebar::MenuComponent < FoxTail::BaseComponent
4
+ renders_many :items, lambda { |options = {}|
5
+ options[:theme] = theme.theme :menu_item
6
+ FoxTail::Sidebar::MenuItemComponent.new options
7
+ }
8
+
9
+ renders_one :menu, lambda { |options = {}|
10
+ options[:theme] = theme.theme :menu
11
+ FoxTail::Sidebar::MenuItemComponent.new options
12
+ }
13
+
14
+ def render?
15
+ items?
16
+ end
17
+
18
+ def before_render
19
+ super
20
+
21
+ html_attributes[:class] = classnames theme.apply(:root, self), html_class
22
+ end
23
+
24
+ def call
25
+ content_tag :ul, html_attributes do
26
+ items.each { |item| concat item }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FoxTail::Sidebar::MenuItemComponent < FoxTail::ClickableComponent
4
+ renders_one :left_visual, types: {
5
+ icon: {
6
+ as: :left_icon,
7
+ renders: lambda { |icon, options = {}|
8
+ options[:class] = classnames theme.apply(:visual, self, { position: :left, type: :svg }),
9
+ options[:class]
10
+
11
+ FoxTail::IconBaseComponent.new icon, options
12
+ }
13
+ },
14
+ svg: {
15
+ as: :left_svg,
16
+ renders: lambda { |path, options = {}|
17
+ options[:class] = classnames theme.apply(:visual, self, { position: :left, type: :svg }),
18
+ options[:class]
19
+
20
+ FoxTail::InlineSvgComponent.new path, options
21
+ }
22
+ },
23
+ image: {
24
+ as: :left_image,
25
+ renders: lambda { |source, options = {}|
26
+ options[:class] = classnames theme.apply(:visual, self, { position: :left, type: :image }),
27
+ options[:class]
28
+
29
+ image_tag source, options
30
+ }
31
+ }
32
+ }
33
+
34
+ renders_one :right_visual, types: {
35
+ icon: {
36
+ as: :right_icon,
37
+ renders: lambda { |icon, options = {}|
38
+ options[:class] = classnames theme.apply(:visual, self, { position: :right, type: :icon }),
39
+ options[:class]
40
+
41
+ FoxTail::IconBaseComponent.new icon, options
42
+ }
43
+ },
44
+ svg: {
45
+ as: :right_svg,
46
+ renders: lambda { |path, options = {}|
47
+ options[:class] = classnames theme.apply(:visual, self, { position: :right, type: :svg }),
48
+ options[:class]
49
+
50
+ FoxTail::InlineSvgComponent.new path, options
51
+ }
52
+ },
53
+ image: {
54
+ as: :right_image,
55
+ renders: lambda { |source, options = {}|
56
+ options[:class] = classnames theme.apply(:visual, self, { position: :right, type: :image }),
57
+ options[:class]
58
+ image_tag source, options
59
+ }
60
+ },
61
+ badge: {
62
+ as: :badge,
63
+ renders: lambda { |options = {}|
64
+ options[:class] = classnames theme.apply(:visual, self, { position: :right, type: :badge }),
65
+ options[:class]
66
+
67
+ FoxTail::BadgeComponent.new options
68
+ }
69
+ }
70
+ }
71
+
72
+ renders_one :menu, lambda { |options = {}|
73
+ options[:theme] = theme.theme :menu
74
+ options[:id] = menu_id
75
+ FoxTail::Sidebar::MenuComponent.new options
76
+ }
77
+
78
+ has_option :id
79
+ has_option :menu_id
80
+ has_option :selected, type: :boolean, default: false
81
+
82
+ def initialize(html_attributes = {})
83
+ super(html_attributes)
84
+
85
+ options[:id] ||= "menu_item_#{SecureRandom.alphanumeric 16}"
86
+ options[:menu_id] ||= "#{options[:id]}_menu"
87
+ end
88
+
89
+ def link?
90
+ menu? ? false : super
91
+ end
92
+
93
+ def before_render
94
+ super
95
+
96
+ if menu?
97
+ with_right_icon "chevron-down" unless right_visual
98
+ end
99
+ end
100
+
101
+ def call
102
+ content_tag :li do
103
+ if menu?
104
+ concat render_collapsible_trigger
105
+ concat render_menu
106
+ else
107
+ render_item
108
+ end
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def render_collapsible_trigger
115
+ render FoxTail::CollapsibleTriggerComponent.new id, "##{menu_id}", open: selected?, theme: theme do |trigger|
116
+ render_item trigger.html_attributes
117
+ end
118
+ end
119
+
120
+ def render_item(attributes = {})
121
+ attributes = stimulus_merger.merge_attributes html_attributes, attributes
122
+
123
+ content_tag root_tag_name, attributes do
124
+ concat left_visual if left_visual?
125
+ concat content_tag(:span, content, class: theme.apply(:content, self))
126
+ concat right_visual if right_visual?
127
+ end
128
+ end
129
+
130
+ def render_menu
131
+ render FoxTail::CollapsibleComponent.new(menu_id, open: selected?).with_content(menu)
132
+ end
133
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FoxTail::SidebarComponent < FoxTail::DrawerComponent
4
+ renders_one :header
5
+
6
+ renders_many :menus, lambda { |options = {}|
7
+ options[:theme] = theme.theme :menu
8
+
9
+ FoxTail::Sidebar::MenuComponent.new options
10
+ }
11
+
12
+ renders_one :divider, lambda { |options = {}|
13
+ options[:class] = classnames theme.apply(:divider, self), options[:class]
14
+
15
+ FoxTail::HrComponent.new options
16
+ }
17
+
18
+ has_option :dividers, type: :boolean, default: true
19
+
20
+ def before_render
21
+ super
22
+
23
+ with_divider if !divider && dividers?
24
+ end
25
+
26
+ def call
27
+ super do
28
+ concat header if header?
29
+ concat render_menus if menus?
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def render_menus
36
+ content_tag :div, class: theme.apply(:menu_container, self) do
37
+ menus.each_with_index do |menu, index|
38
+ concat divider if divider? && !index.zero?
39
+ concat menu
40
+ end
41
+ end
42
+ end
43
+
44
+ class << self
45
+ def stimulus_controller_name
46
+ :drawer
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ menu_container:
2
+ dividers?: divider-y divider-gray-200 dark:divider-gray-700 space-y-4
3
+ _menu_item: &menu_item
4
+ root:
5
+ base: &menu_item_root_base >-
6
+ flex items-center gap-3 w-full p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group
7
+ content:
8
+ base: truncate whitespace-nowrap flex-grow-1 flex-shrink-1
9
+ visual:
10
+ position:
11
+ left: >-
12
+ w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white
13
+ right: ml-auto
14
+ menu:
15
+ root:
16
+ base: space-y-2 font-medium
17
+ menu_item:
18
+ <<: *menu_item
19
+ menu:
20
+ root:
21
+ base: py-2 space-y-2
22
+ menu_item:
23
+ <<: *menu_item
24
+ root:
25
+ base: !concat [*menu_item_root_base, "pl-11"]
@@ -12,10 +12,6 @@ class FoxTail::Table::RowComponent < FoxTail::BaseComponent
12
12
  has_option :hover, type: :boolean, default: false
13
13
  has_option :border, type: :boolean, default: true
14
14
 
15
- def even?
16
- position.is_a?(Numeric) && position.even?
17
- end
18
-
19
15
  def before_render
20
16
  super
21
17
 
@@ -13,19 +13,19 @@ class FoxTail::TableComponent < FoxTail::BaseComponent
13
13
  }
14
14
 
15
15
  renders_one :header, lambda { |options = {}|
16
- options = options.merge self.options
16
+ options = options.merge section_options
17
17
  options[:theme] = theme.theme :header
18
18
  FoxTail::Table::HeaderComponent.new options
19
19
  }
20
20
 
21
21
  renders_many :rows, lambda { |options = {}|
22
- options = options.merge self.options
22
+ options = options.merge section_options
23
23
  options[:theme] = theme.theme :row
24
24
  FoxTail::Table::RowComponent.new options
25
25
  }
26
26
 
27
27
  renders_one :footer, lambda { |options = {}|
28
- options = options.merge self.options
28
+ options = options.merge section_options
29
29
  options[:theme] = theme.theme :footer
30
30
  FoxTail::Table::RowComponent.new options
31
31
  }
@@ -39,4 +39,10 @@ class FoxTail::TableComponent < FoxTail::BaseComponent
39
39
 
40
40
  html_attributes[:class] = classnames theme.apply(:root, self), html_class
41
41
  end
42
+
43
+ private
44
+
45
+ def section_options
46
+ self.options.slice :highlight, :hover, :border
47
+ end
42
48
  end
@@ -11,7 +11,7 @@ class FoxTail::TextareaComponent < FoxTail::InputBaseComponent
11
11
  end
12
12
 
13
13
  def call
14
- content_tag :textarea, content, html_attributes
14
+ content_tag :textarea, object_name? ? value_from_object : content, html_attributes
15
15
  end
16
16
 
17
17
  class AutoResizeStimulusController < FoxTail::StimulusController
@@ -125,18 +125,11 @@ class FoxTail::FormBuilder < ActionView::Helpers::FormBuilder
125
125
  end
126
126
 
127
127
  def select(method, choices = nil, options = {}, html_options = {}, &block)
128
- include_blank = options.delete :include_blank
129
- prompt = options.delete :prompt
130
128
  options = objectify_component_options(method, options.merge(html_options))
129
+ options[:placeholder] = options.delete(:placeholder) || options.delete(:prompt)
131
130
  choices = choices.to_a if choices.is_a? Range
132
131
 
133
132
  @template.render FoxTail::SelectComponent.new(options) do |select|
134
- if !!prompt
135
- select.with_placeholder prompt_text(prompt)
136
- elsif !!include_blank
137
- select.with_placeholder "", disabled: false
138
- end
139
-
140
133
  if choices.present?
141
134
  choices.each do |choice|
142
135
  choice_options = option_html_attributes choice
@@ -145,7 +138,7 @@ class FoxTail::FormBuilder < ActionView::Helpers::FormBuilder
145
138
  end
146
139
  end
147
140
 
148
- capture select, &block if block
141
+ @template.capture select, &block if block
149
142
  end
150
143
  end
151
144
 
@@ -172,8 +165,8 @@ class FoxTail::FormBuilder < ActionView::Helpers::FormBuilder
172
165
 
173
166
  def button(value = nil, options = {}, &block)
174
167
  if value.is_a? Hash
175
- value = nil
176
168
  options = value
169
+ value = nil
177
170
  end
178
171
 
179
172
  options = objectify_component_options nil, options
@@ -1,3 +1,3 @@
1
1
  module FoxTail
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fox_tail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Fawks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-17 00:00:00.000000000 Z
11
+ date: 2024-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -1065,6 +1065,10 @@ files:
1065
1065
  - app/components/fox_tail/select/option_component.rb
1066
1066
  - app/components/fox_tail/select/option_group_component.rb
1067
1067
  - app/components/fox_tail/select_component.rb
1068
+ - app/components/fox_tail/sidebar/menu_component.rb
1069
+ - app/components/fox_tail/sidebar/menu_item_component.rb
1070
+ - app/components/fox_tail/sidebar_component.rb
1071
+ - app/components/fox_tail/sidebar_component.theme.yml
1068
1072
  - app/components/fox_tail/spinner_component.rb
1069
1073
  - app/components/fox_tail/spinner_component.theme.yml
1070
1074
  - app/components/fox_tail/stepper/step_component.html.erb