fox_tail 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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