proscenium-ui 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/lib/proscenium/ui/badge/index.css +75 -0
- data/lib/proscenium/ui/badge.rb +32 -0
- data/lib/proscenium/ui/breadcrumbs/computed_element.rb +71 -0
- data/lib/proscenium/ui/breadcrumbs/control.rb +103 -0
- data/lib/proscenium/ui/breadcrumbs/element.rb +16 -0
- data/lib/proscenium/ui/breadcrumbs/index.css +84 -0
- data/lib/proscenium/ui/breadcrumbs.rb +136 -0
- data/lib/proscenium/ui/combobox/index.css +162 -0
- data/lib/proscenium/ui/combobox/index.js +420 -0
- data/lib/proscenium/ui/combobox.rb +186 -0
- data/lib/proscenium/ui/component.rb +22 -0
- data/lib/proscenium/ui/flash/index.css +1 -0
- data/lib/proscenium/ui/flash/index.js +77 -0
- data/lib/proscenium/ui/flash.rb +15 -0
- data/lib/proscenium/ui/form/field_methods.rb +95 -0
- data/lib/proscenium/ui/form/fields/base.rb +189 -0
- data/lib/proscenium/ui/form/fields/checkbox/index.jsx +48 -0
- data/lib/proscenium/ui/form/fields/checkbox/index.module.css +9 -0
- data/lib/proscenium/ui/form/fields/checkbox/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/checkbox.rb +32 -0
- data/lib/proscenium/ui/form/fields/combobox.rb +117 -0
- data/lib/proscenium/ui/form/fields/date.module.css +27 -0
- data/lib/proscenium/ui/form/fields/datetime.rb +15 -0
- data/lib/proscenium/ui/form/fields/hidden.rb +9 -0
- data/lib/proscenium/ui/form/fields/input/index.jsx +71 -0
- data/lib/proscenium/ui/form/fields/input/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/input/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/input.rb +14 -0
- data/lib/proscenium/ui/form/fields/radio_group.rb +175 -0
- data/lib/proscenium/ui/form/fields/radio_input/index.jsx +44 -0
- data/lib/proscenium/ui/form/fields/radio_input/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/radio_input/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/radio_input.rb +17 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.css +23 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.js +6 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.rb +18 -0
- data/lib/proscenium/ui/form/fields/select.jsx +47 -0
- data/lib/proscenium/ui/form/fields/select.module.css +46 -0
- data/lib/proscenium/ui/form/fields/select.rb +302 -0
- data/lib/proscenium/ui/form/fields/tel.css +297 -0
- data/lib/proscenium/ui/form/fields/tel.js +83 -0
- data/lib/proscenium/ui/form/fields/tel.rb +54 -0
- data/lib/proscenium/ui/form/fields/textarea/index.jsx +50 -0
- data/lib/proscenium/ui/form/fields/textarea/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/textarea/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/textarea.rb +18 -0
- data/lib/proscenium/ui/form/index.css +52 -0
- data/lib/proscenium/ui/form/translation.rb +71 -0
- data/lib/proscenium/ui/form.rb +197 -0
- data/lib/proscenium/ui/railtie.rb +23 -0
- data/lib/proscenium/ui/ujs/class.js +15 -0
- data/lib/proscenium/ui/ujs/data_confirm.js +23 -0
- data/lib/proscenium/ui/ujs/data_disable_with.js +68 -0
- data/lib/proscenium/ui/ujs/index.js +10 -0
- data/lib/proscenium/ui/version.rb +7 -0
- data/lib/proscenium/ui.rb +36 -0
- metadata +177 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ca2623aea45e90824299d7c043f1ed80814fc18dea6598c43a798cc9afa33a7f
|
|
4
|
+
data.tar.gz: 8c4140e7664ad61705fee670455761b2ccef3aa429b149c5733f14e374eff45a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 83809d6e7fbe7a7bf033ef58a68468c690abe17034e1327ba326dea1d13218a571988a621c23ce339bab0ecb04345084c605cd658e20ec80cb35d9dd9e06ba8f
|
|
7
|
+
data.tar.gz: 407fee2ddd35600f3722b2abb7b3794288248c0b07ff666fb318555bbb51826e4c84c721b0e24c4750e6dbb07fdfc5100a3241f8b840b38d816664efa76340ce
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Joel Moss
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Proscenium::UI
|
|
2
|
+
Short description and motivation.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
How to use my plugin.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem "proscenium-ui"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install proscenium-ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Contributing
|
|
25
|
+
Contribution directions go here.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
@layer pui {
|
|
2
|
+
pui-badge {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
vertical-align: baseline;
|
|
6
|
+
gap: 0.25em;
|
|
7
|
+
padding: 0.15em 0.5em;
|
|
8
|
+
border-radius: 0.25em;
|
|
9
|
+
font-size: 0.875em;
|
|
10
|
+
font-variant-numeric: tabular-nums;
|
|
11
|
+
line-height: 1.4;
|
|
12
|
+
white-space: nowrap;
|
|
13
|
+
background-color: var(--puiBadge--bg, color-mix(in srgb, currentColor 10%, transparent));
|
|
14
|
+
color: var(--puiBadge--color, inherit);
|
|
15
|
+
border: var(--puiBadge--border, 1px solid color-mix(in srgb, currentColor 20%, transparent));
|
|
16
|
+
|
|
17
|
+
&[data-size='sm'] {
|
|
18
|
+
font-size: 0.75em;
|
|
19
|
+
padding: 0.1em 0.35em;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&[data-size='lg'] {
|
|
23
|
+
font-size: 1em;
|
|
24
|
+
padding: 0.2em 0.6em;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&[data-variant='success'] {
|
|
28
|
+
background-color: var(
|
|
29
|
+
--puiBadge--success-bg,
|
|
30
|
+
color-mix(in srgb, light-dark(#15803d, #4ade80) 15%, transparent)
|
|
31
|
+
);
|
|
32
|
+
color: var(--puiBadge--success-color, light-dark(#15803d, #4ade80));
|
|
33
|
+
border-color: var(
|
|
34
|
+
--puiBadge--success-border-color,
|
|
35
|
+
color-mix(in srgb, light-dark(#15803d, #4ade80) 30%, transparent)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&[data-variant='warning'] {
|
|
40
|
+
background-color: var(
|
|
41
|
+
--puiBadge--warning-bg,
|
|
42
|
+
color-mix(in srgb, light-dark(#a16207, #fbbf24) 15%, transparent)
|
|
43
|
+
);
|
|
44
|
+
color: var(--puiBadge--warning-color, light-dark(#a16207, #fbbf24));
|
|
45
|
+
border-color: var(
|
|
46
|
+
--puiBadge--warning-border-color,
|
|
47
|
+
color-mix(in srgb, light-dark(#a16207, #fbbf24) 30%, transparent)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&[data-variant='danger'] {
|
|
52
|
+
background-color: var(
|
|
53
|
+
--puiBadge--danger-bg,
|
|
54
|
+
color-mix(in srgb, light-dark(#dc2626, #f87171) 15%, transparent)
|
|
55
|
+
);
|
|
56
|
+
color: var(--puiBadge--danger-color, light-dark(#dc2626, #f87171));
|
|
57
|
+
border-color: var(
|
|
58
|
+
--puiBadge--danger-border-color,
|
|
59
|
+
color-mix(in srgb, light-dark(#dc2626, #f87171) 30%, transparent)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&[data-variant='info'] {
|
|
64
|
+
background-color: var(
|
|
65
|
+
--puiBadge--info-bg,
|
|
66
|
+
color-mix(in srgb, light-dark(#2563eb, #60a5fa) 15%, transparent)
|
|
67
|
+
);
|
|
68
|
+
color: var(--puiBadge--info-color, light-dark(#2563eb, #60a5fa));
|
|
69
|
+
border-color: var(
|
|
70
|
+
--puiBadge--info-border-color,
|
|
71
|
+
color-mix(in srgb, light-dark(#2563eb, #60a5fa) 30%, transparent)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Proscenium::UI
|
|
4
|
+
class Badge < Component
|
|
5
|
+
register_element :pui_badge
|
|
6
|
+
|
|
7
|
+
# @param text [String] The text content of the badge.
|
|
8
|
+
prop :text, String, :positional
|
|
9
|
+
|
|
10
|
+
# @param variant [Symbol] The visual style variant. (:default, :success, :warning, :danger,
|
|
11
|
+
# :info)
|
|
12
|
+
prop :variant, _Union(:default, :success, :warning, :danger, :info), default: -> { :default }
|
|
13
|
+
|
|
14
|
+
# @param size [Symbol] The size of the badge. (:sm, :md, :lg)
|
|
15
|
+
prop :size, _Union(:sm, :md, :lg), default: -> { :md }
|
|
16
|
+
|
|
17
|
+
# @param rest [Hash] Additional HTML attributes passed to the root element.
|
|
18
|
+
prop :rest, Hash, :**
|
|
19
|
+
|
|
20
|
+
def self.source_path
|
|
21
|
+
super.sub_ext('').join('index.rb')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def view_template
|
|
25
|
+
data = {}
|
|
26
|
+
data[:variant] = @variant if @variant != :default
|
|
27
|
+
data[:size] = @size if @size != :md
|
|
28
|
+
|
|
29
|
+
pui_badge(**{ data: }.deep_merge(@rest)) { @text }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Proscenium::UI::Breadcrumbs
|
|
4
|
+
class ComputedElement
|
|
5
|
+
def initialize(element, context)
|
|
6
|
+
@element = element
|
|
7
|
+
@context = context
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
delegate :options, to: :@element
|
|
11
|
+
|
|
12
|
+
# If name is a Symbol of a controller method, that method is called.
|
|
13
|
+
# If name is a Symbol of a controller instance variable, that variable is returned.
|
|
14
|
+
# If name is a Proc, it is executed in the context of the controller instance.
|
|
15
|
+
#
|
|
16
|
+
# @return [String] the content of the breadcrumb element.
|
|
17
|
+
def name
|
|
18
|
+
@name ||= case name = @element.name
|
|
19
|
+
when Symbol
|
|
20
|
+
if name.to_s.starts_with?('@')
|
|
21
|
+
name = get_instance_variable(name)
|
|
22
|
+
name.respond_to?(:for_breadcrumb) ? name.for_breadcrumb : name.to_s
|
|
23
|
+
else
|
|
24
|
+
res = @context.controller.send(name)
|
|
25
|
+
res.try(:for_breadcrumb) || res.to_s
|
|
26
|
+
end
|
|
27
|
+
when Proc
|
|
28
|
+
@context.controller.instance_exec(&name)
|
|
29
|
+
else
|
|
30
|
+
name.respond_to?(:for_breadcrumb) ? name.for_breadcrumb : name.to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# If path is a Symbol of a controller method, that method is called.
|
|
35
|
+
# If path is a Symbol of a controller instance variable, that variable is returned.
|
|
36
|
+
# If path is an Array, each element is processed as above.
|
|
37
|
+
# If path is a Proc, it is executed in the context of the controller instance.
|
|
38
|
+
#
|
|
39
|
+
# No matter what, the result is always passed to `url_for` before being returned.
|
|
40
|
+
#
|
|
41
|
+
# @return [String] the URL for the element
|
|
42
|
+
def path
|
|
43
|
+
@path ||= if !@element.path.nil?
|
|
44
|
+
case path = @element.path
|
|
45
|
+
when Array
|
|
46
|
+
path.map! { |x| x.to_s.starts_with?('@') ? get_instance_variable(x) : x }
|
|
47
|
+
when Symbol
|
|
48
|
+
if path.to_s.starts_with?('@')
|
|
49
|
+
path = get_instance_variable(path)
|
|
50
|
+
elsif @context.controller.respond_to?(path, true)
|
|
51
|
+
path = @context.controller.send(path)
|
|
52
|
+
end
|
|
53
|
+
when Proc
|
|
54
|
+
path = @context.controller.instance_exec(&path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
@context.url_for path
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def get_instance_variable(element)
|
|
64
|
+
if !@context.instance_variable_defined?(element)
|
|
65
|
+
raise NameError, "undefined instance variable `#{element}' for breadcrumb", caller
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@context.instance_variable_get element
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Proscenium::UI::Breadcrumbs
|
|
4
|
+
# Include this module in your controller to add support for adding breadcrumb elements. You can
|
|
5
|
+
# then use the `add_breadcrumb` and `prepend_breadcrumb` class methods to append and/or prepend
|
|
6
|
+
# breadcrumb elements.
|
|
7
|
+
module Control
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
include ActionView::Helpers::SanitizeHelper
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
helper_method :breadcrumbs_as_json, :breadcrumbs_for_title if respond_to?(:helper_method)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
# Appends a new breadcrumb element into the collection.
|
|
17
|
+
#
|
|
18
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
|
19
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
|
20
|
+
# breadcrumb.
|
|
21
|
+
# @param options [Hash] Filter options to pass through to the before_action filter. Options
|
|
22
|
+
# include :if, :unless, :only, and :except. Any options not included in the filter options
|
|
23
|
+
# will be passed as options to the breadcrumb element. For example, you could pass a :class
|
|
24
|
+
# option to add a CSS class to the breadcrumb element.
|
|
25
|
+
def add_breadcrumb(name, path = nil, **options)
|
|
26
|
+
filter_options = options.slice(:if, :unless, :only, :except)
|
|
27
|
+
element_options = options.except(:if, :unless, :only, :except)
|
|
28
|
+
|
|
29
|
+
before_action(filter_options) do |controller|
|
|
30
|
+
controller.send :add_breadcrumb, name, path, element_options
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Prepend a new breadcrumb element into the collection.
|
|
35
|
+
#
|
|
36
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
|
37
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
|
38
|
+
# breadcrumb.
|
|
39
|
+
# @param options [Hash] Filter options to pass through to the before_action filter. Options
|
|
40
|
+
# include :if, :unless, :only, and :except. Any options not included in the filter options
|
|
41
|
+
# will be passed as options to the breadcrumb element. For example, you could pass a :class
|
|
42
|
+
# option to add a CSS class to the breadcrumb element.
|
|
43
|
+
def prepend_breadcrumb(name, path = nil, **options)
|
|
44
|
+
filter_options = options.slice(:if, :unless, :only, :except)
|
|
45
|
+
element_options = options.except(:if, :unless, :only, :except)
|
|
46
|
+
|
|
47
|
+
before_action(filter_options) do |controller|
|
|
48
|
+
controller.send :prepend_breadcrumb, name, path, element_options
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Pushes a new breadcrumb element into the collection.
|
|
54
|
+
#
|
|
55
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
|
56
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
|
57
|
+
# breadcrumb.
|
|
58
|
+
# @param options [Hash]
|
|
59
|
+
def add_breadcrumb(name, path = nil, options = {})
|
|
60
|
+
breadcrumbs << Element.new(name, path, options)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Prepend a new breadcrumb element into the collection.
|
|
64
|
+
#
|
|
65
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
|
66
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
|
67
|
+
# breadcrumb.
|
|
68
|
+
# @param options [Hash]
|
|
69
|
+
def prepend_breadcrumb(name, path = nil, options = {})
|
|
70
|
+
breadcrumbs.prepend Element.new(name, path, options)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def breadcrumbs
|
|
74
|
+
@breadcrumbs ||= []
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def breadcrumbs_as_json
|
|
78
|
+
computed_breadcrumbs.map do |ele|
|
|
79
|
+
path = ele.path
|
|
80
|
+
|
|
81
|
+
{ name: ele.name, path: ele.path.nil? || view_context.current_page?(path) ? nil : path }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param primary [Boolean] whether to return only the primary breadcrumb.
|
|
86
|
+
def breadcrumbs_for_title(primary: false)
|
|
87
|
+
names = computed_breadcrumbs.map(&:name)
|
|
88
|
+
return names.pop if primary
|
|
89
|
+
|
|
90
|
+
out = [names.pop]
|
|
91
|
+
out << names.join(': ') if !names.empty?
|
|
92
|
+
strip_tags out.join(' - ')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def computed_breadcrumbs
|
|
98
|
+
@computed_breadcrumbs ||= breadcrumbs.map do |ele|
|
|
99
|
+
ComputedElement.new ele, view_context
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Represents a navigation element in the breadcrumb collection.
|
|
4
|
+
class Proscenium::UI::Breadcrumbs::Element
|
|
5
|
+
attr_accessor :name, :path, :options
|
|
6
|
+
|
|
7
|
+
# @param name [String] the element/link name
|
|
8
|
+
# @param path [String] the element/link URL
|
|
9
|
+
# @param options [Hash] the element/link options
|
|
10
|
+
# @return [Element]
|
|
11
|
+
def initialize(name, path = nil, options = {})
|
|
12
|
+
self.name = name
|
|
13
|
+
self.path = path
|
|
14
|
+
self.options = options
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
@define-mixin _separator {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
content: '';
|
|
4
|
+
height: 1em;
|
|
5
|
+
width: 1em;
|
|
6
|
+
-webkit-mask: var(--puiBreadcrumbs--separator, var(--_puiBreadcrumbs--separator)) no-repeat center
|
|
7
|
+
center;
|
|
8
|
+
mask: var(--puiBreadcrumbs--separator, var(--_puiBreadcrumbs--separator)) no-repeat center center;
|
|
9
|
+
vertical-align: sub;
|
|
10
|
+
background-color: var(--puiBreadcrumbs--separator-color, var(--_puiBreadcrumbs--separator-color));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@layer pui {
|
|
14
|
+
/*
|
|
15
|
+
* Properties:
|
|
16
|
+
*
|
|
17
|
+
* --puiBreadcrumbs--link-color: LinkText;
|
|
18
|
+
* --puiBreadcrumbs--link-hover-color: HighlightText;
|
|
19
|
+
* --puiBreadcrumbs--separator-color: GrayText;
|
|
20
|
+
* --puiBreadcrumbs--separator: url("...");
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
pui-breadcrumbs {
|
|
24
|
+
--_puiBreadcrumbs--separator-color: GrayText;
|
|
25
|
+
--_puiBreadcrumbs--separator: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z' clip-rule='evenodd'/%3E%3C/svg%3E");
|
|
26
|
+
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
align-items: center;
|
|
30
|
+
|
|
31
|
+
pui-breadcrumbs-home,
|
|
32
|
+
pui-breadcrumbs-element {
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
|
|
37
|
+
@media (max-width: 426px) {
|
|
38
|
+
&:not(:nth-last-child(2)) {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&:nth-last-child(2)::before {
|
|
43
|
+
@mixin _separator;
|
|
44
|
+
margin: 0 0.5em 0 0;
|
|
45
|
+
transform: rotate(180deg);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@media (min-width: 427px) {
|
|
50
|
+
&:not(:last-child)::after {
|
|
51
|
+
@mixin _separator;
|
|
52
|
+
margin: 0 0.5em;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&:last-child,
|
|
57
|
+
&:last-child > a {
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
text-transform: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
a {
|
|
63
|
+
color: var(--puiBreadcrumbs--link-color, revert);
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
|
|
67
|
+
&:hover {
|
|
68
|
+
color: var(--puiBreadcrumbs--link-hover-color, revert);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@media (pointer: coarse) {
|
|
72
|
+
min-height: 44px;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pui-breadcrumbs-home {
|
|
78
|
+
svg {
|
|
79
|
+
height: 1em;
|
|
80
|
+
width: 1em;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Proscenium::UI
|
|
4
|
+
# Provides breadcrumb functionality for controllers and views. Breadcrumbs are a type of
|
|
5
|
+
# navigation that show the user where they are in the application's hierarchy.
|
|
6
|
+
# The `Proscenium::UI::Breadcrumbs::Control` module provides the `add_breadcrumb` and
|
|
7
|
+
# `prepend_breadcrumb` class methods for adding breadcrumb elements, and is intended to be
|
|
8
|
+
# included in your controllers.
|
|
9
|
+
#
|
|
10
|
+
# The `add_breadcrumb` method adds a new breadcrumb element to the end of the collection, while
|
|
11
|
+
# the `prepend_breadcrumb` method adds a new breadcrumb element to the beginning of the
|
|
12
|
+
# collection. Both methods take a name, and path as arguments. The name argument is the name or
|
|
13
|
+
# content of the breadcrumb, while the path argument is the path (route) to use as the HREF for
|
|
14
|
+
# the breadcrumb.
|
|
15
|
+
#
|
|
16
|
+
# class UsersController < ApplicationController
|
|
17
|
+
# include Proscenium::UI::Breadcrumbs::Control
|
|
18
|
+
# add_breadcrumb 'Users', :users_path
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Display the breadcrumbs in your view:
|
|
22
|
+
#
|
|
23
|
+
# render Proscenium::UI::Breadcrumbs
|
|
24
|
+
#
|
|
25
|
+
# At its simplest, you can add a breadcrumb with a name of "Users", and a path of "/users" like
|
|
26
|
+
# this:
|
|
27
|
+
#
|
|
28
|
+
# add_breadcrumb 'Users', '/users'
|
|
29
|
+
#
|
|
30
|
+
# The value of the path is always passed to `url_for` before being rendered. It is also optional,
|
|
31
|
+
# and if omitted, the breadcrumb will be rendered as plain text.
|
|
32
|
+
#
|
|
33
|
+
# Both name and path can be given a Symbol, which can be used to call a method of the same name on
|
|
34
|
+
# the controller. If a Symbol is given as the path, and no method of the same name exists, then
|
|
35
|
+
# `url_for` will be called with the Symbol as the argument. Likewise, if an Array is given as the
|
|
36
|
+
# path, then `url_for` will be called with the Array as the argument.
|
|
37
|
+
#
|
|
38
|
+
# If a Symbol is given as the path or name, and it begins with `@` (eg. `:@foo`), then the
|
|
39
|
+
# instance variable of the same name will be called.
|
|
40
|
+
#
|
|
41
|
+
# add_breadcrumb :@foo, :@bar
|
|
42
|
+
#
|
|
43
|
+
# A Proc can also be given as the name and/or path. The Proc will be called within the context of
|
|
44
|
+
# the controller.
|
|
45
|
+
#
|
|
46
|
+
# add_breadcrumb -> { @foo }, -> { @bar }
|
|
47
|
+
#
|
|
48
|
+
# Passing an object that responds to `#for_breadcrumb` as the name will call that method on the
|
|
49
|
+
# object to get the breadcrumb name.
|
|
50
|
+
#
|
|
51
|
+
class Breadcrumbs < Component
|
|
52
|
+
include Phlex::Rails::Helpers::URLFor
|
|
53
|
+
|
|
54
|
+
register_element :pui_breadcrumbs
|
|
55
|
+
register_element :pui_breadcrumbs_home
|
|
56
|
+
register_element :pui_breadcrumbs_element
|
|
57
|
+
|
|
58
|
+
# @param home_path [String, Symbol] The path (route) to use as the HREF for the home segment.
|
|
59
|
+
prop :home_path, _Union(String, Symbol), default: -> { :root }
|
|
60
|
+
|
|
61
|
+
# @param with_home [Boolean] Whether to show the home segment. Set to false to hide it.
|
|
62
|
+
prop :with_home, _Boolean, default: -> { true }
|
|
63
|
+
|
|
64
|
+
def self.source_path
|
|
65
|
+
super.sub_ext('').join('index.rb')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# TODO: For if and when we need to pass instance level css module.
|
|
69
|
+
#
|
|
70
|
+
# prop :css_module_path, _Nilable(Pathname)
|
|
71
|
+
#
|
|
72
|
+
# def css_module_path
|
|
73
|
+
# @css_module_path ||= self.class.css_module_path
|
|
74
|
+
# end
|
|
75
|
+
|
|
76
|
+
def view_template
|
|
77
|
+
pui_breadcrumbs do
|
|
78
|
+
if @with_home
|
|
79
|
+
pui_breadcrumbs_home do
|
|
80
|
+
yield if block_given?
|
|
81
|
+
home_template
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
breadcrumbs.each do |ce|
|
|
86
|
+
pui_breadcrumbs_element(**ce.options) do
|
|
87
|
+
path = ce.path
|
|
88
|
+
path.nil? ? ce.name : a(href: url_for(path)) { ce.name }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Override this to customise the home breadcrumb. You can call super with a block to use the
|
|
95
|
+
# default template, but with custom content.
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# def home_template
|
|
99
|
+
# super { 'hello' }
|
|
100
|
+
# end
|
|
101
|
+
def home_template(&block)
|
|
102
|
+
a href: url_for(@home_path) do
|
|
103
|
+
if block
|
|
104
|
+
yield
|
|
105
|
+
else
|
|
106
|
+
svg role: 'img', aria_label: 'Home', xmlns: 'http://www.w3.org/2000/svg',
|
|
107
|
+
viewBox: '0 0 24 24', fill: 'currentColor' do |s|
|
|
108
|
+
s.path d: <<~SVG.delete("\n")
|
|
109
|
+
M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69a.75.75 0 1 0 1.06-1.061l-8.689
|
|
110
|
+
-8.69a2.25 2.25 0 0 0-3.182 0l-8.69 8.69a.75.75 0 1 0 1.061 1.06l8.69-8.69Z
|
|
111
|
+
SVG
|
|
112
|
+
s.path d: <<~SVG.delete("\n")
|
|
113
|
+
M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875
|
|
114
|
+
1.875H15.75a.75.75 0 0 1-.75-.75v-4.5a.75.75 0 0 0-.75-.75h-4.5a.75.75 0 0
|
|
115
|
+
0-.75.75V21a.75.75 0 0 1-.75.75H5.625a1.875 1.875 0 0 1-1.875-1.875v-6.198
|
|
116
|
+
a2.29 2.29 0 0 0 .091-.086L12 5.432Z
|
|
117
|
+
SVG
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Don't render if @hide_breadcrumbs is true.
|
|
124
|
+
def render?
|
|
125
|
+
controller.instance_variable_get(:@hide_breadcrumbs) != true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def breadcrumbs
|
|
131
|
+
controller.breadcrumbs.map do |e|
|
|
132
|
+
Breadcrumbs::ComputedElement.new e, view_context
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|