proscenium 0.18.0-arm64-darwin → 0.19.0-arm64-darwin
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 +25 -174
- data/lib/proscenium/builder.rb +36 -100
- data/lib/proscenium/bundled_gems.rb +37 -0
- data/lib/proscenium/css_module/path.rb +2 -1
- data/lib/proscenium/css_module/transformer.rb +1 -1
- data/lib/proscenium/css_module.rb +16 -9
- data/lib/proscenium/ensure_loaded.rb +14 -10
- data/lib/proscenium/ext/proscenium +0 -0
- data/lib/proscenium/ext/proscenium.h +9 -36
- data/lib/proscenium/helper.rb +4 -22
- data/lib/proscenium/importer.rb +39 -21
- data/lib/proscenium/log_subscriber.rb +11 -10
- data/lib/proscenium/middleware/base.rb +11 -6
- data/lib/proscenium/middleware/esbuild.rb +8 -9
- data/lib/proscenium/middleware/ruby_gems.rb +23 -0
- data/lib/proscenium/middleware.rb +26 -24
- data/lib/proscenium/monkey.rb +5 -14
- data/lib/proscenium/railtie.rb +11 -53
- data/lib/proscenium/{libs/react-manager → react-manager}/index.jsx +3 -22
- data/lib/proscenium/react_componentable.rb +2 -3
- data/lib/proscenium/resolver.rb +14 -23
- data/lib/proscenium/side_load.rb +41 -74
- data/lib/proscenium/utils.rb +33 -0
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium.rb +2 -12
- metadata +11 -99
- data/lib/proscenium/core_ext/object/css_module_ivars.rb +0 -19
- data/lib/proscenium/css_module/rewriter.rb +0 -44
- data/lib/proscenium/libs/custom_element.js +0 -54
- data/lib/proscenium/libs/stimulus-loading.js +0 -65
- data/lib/proscenium/libs/test.js +0 -1
- data/lib/proscenium/libs/ujs/class.js +0 -15
- data/lib/proscenium/libs/ujs/data_confirm.js +0 -23
- data/lib/proscenium/libs/ujs/data_disable_with.js +0 -68
- data/lib/proscenium/libs/ujs/index.js +0 -9
- data/lib/proscenium/middleware/engines.rb +0 -45
- data/lib/proscenium/middleware/runtime.rb +0 -18
- data/lib/proscenium/phlex/asset_inclusions.rb +0 -17
- data/lib/proscenium/phlex/css_modules.rb +0 -79
- data/lib/proscenium/phlex/react_component.rb +0 -32
- data/lib/proscenium/phlex.rb +0 -42
- data/lib/proscenium/ui/breadcrumbs/component.module.css +0 -14
- data/lib/proscenium/ui/breadcrumbs/component.rb +0 -73
- data/lib/proscenium/ui/breadcrumbs/computed_element.rb +0 -69
- data/lib/proscenium/ui/breadcrumbs/control.rb +0 -95
- data/lib/proscenium/ui/breadcrumbs/mixins.css +0 -83
- data/lib/proscenium/ui/breadcrumbs.rb +0 -72
- data/lib/proscenium/ui/component.rb +0 -7
- data/lib/proscenium/ui/test.js +0 -1
- data/lib/proscenium/ui.rb +0 -8
- data/lib/proscenium/view_component/css_modules.rb +0 -11
- data/lib/proscenium/view_component/react_component.rb +0 -22
- data/lib/proscenium/view_component/sideload.rb +0 -4
- data/lib/proscenium/view_component.rb +0 -38
- /data/lib/proscenium/{libs/react-manager → react-manager}/react.js +0 -0
@@ -1,15 +0,0 @@
|
|
1
|
-
import DataConfirm from "./data_confirm";
|
2
|
-
import DataDisableWith from "./data_disable_with";
|
3
|
-
|
4
|
-
export default class UJS {
|
5
|
-
constructor() {
|
6
|
-
this.dc = new DataConfirm();
|
7
|
-
this.ddw = new DataDisableWith();
|
8
|
-
|
9
|
-
document.addEventListener("submit", this, { capture: true });
|
10
|
-
}
|
11
|
-
|
12
|
-
handleEvent(event) {
|
13
|
-
this.dc.onSubmit(event) && this.ddw.onSubmit(event);
|
14
|
-
}
|
15
|
-
}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
export default class DataConfirm {
|
2
|
-
onSubmit = (event) => {
|
3
|
-
if (
|
4
|
-
!event.target.matches("[data-turbo=true]") &&
|
5
|
-
event.submitter &&
|
6
|
-
"confirm" in event.submitter.dataset
|
7
|
-
) {
|
8
|
-
const v = event.submitter.dataset.confirm;
|
9
|
-
|
10
|
-
if (
|
11
|
-
v !== "false" &&
|
12
|
-
!confirm(v === "true" || v === "" ? "Are you sure?" : v)
|
13
|
-
) {
|
14
|
-
event.preventDefault();
|
15
|
-
event.stopPropagation();
|
16
|
-
event.stopImmediatePropagation();
|
17
|
-
return false;
|
18
|
-
}
|
19
|
-
}
|
20
|
-
|
21
|
-
return true;
|
22
|
-
};
|
23
|
-
}
|
@@ -1,68 +0,0 @@
|
|
1
|
-
export default class DataDisableWith {
|
2
|
-
onSubmit = event => {
|
3
|
-
const target = event.target
|
4
|
-
const formId = target.id
|
5
|
-
|
6
|
-
if (target.matches('[data-turbo=true]')) return
|
7
|
-
|
8
|
-
const submitElements = Array.from(
|
9
|
-
target.querySelectorAll(
|
10
|
-
['input[type=submit][data-disable-with]', 'button[type=submit][data-disable-with]'].join(
|
11
|
-
', '
|
12
|
-
)
|
13
|
-
)
|
14
|
-
)
|
15
|
-
|
16
|
-
submitElements.push(
|
17
|
-
...Array.from(
|
18
|
-
document.querySelectorAll(
|
19
|
-
[
|
20
|
-
`input[type=submit][data-disable-with][form='${formId}']`,
|
21
|
-
`button[type=submit][data-disable-with][form='${formId}']`
|
22
|
-
].join(', ')
|
23
|
-
)
|
24
|
-
)
|
25
|
-
)
|
26
|
-
|
27
|
-
for (const ele of submitElements) {
|
28
|
-
if (ele.hasAttribute('form') && ele.getAttribute('form') !== target.id) continue
|
29
|
-
|
30
|
-
this.#disableButton(ele)
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
#disableButton(ele) {
|
35
|
-
const defaultTextValue = 'Please wait...'
|
36
|
-
let textValue = ele.dataset.disableWith || defaultTextValue
|
37
|
-
if (textValue === 'false') return
|
38
|
-
if (textValue === 'true') {
|
39
|
-
textValue = defaultTextValue
|
40
|
-
}
|
41
|
-
|
42
|
-
ele.disabled = true
|
43
|
-
|
44
|
-
if (ele.matches('button')) {
|
45
|
-
ele.dataset.valueBeforeDisabled = ele.innerHTML
|
46
|
-
ele.innerHTML = textValue
|
47
|
-
} else {
|
48
|
-
ele.dataset.valueBeforeDisabled = ele.value
|
49
|
-
ele.value = textValue
|
50
|
-
}
|
51
|
-
|
52
|
-
if (ele.resetDisableWith === undefined) {
|
53
|
-
// This function can be called on the element to reset the disabled state. Useful for when
|
54
|
-
// form submission fails, and the button should be re-enabled.
|
55
|
-
ele.resetDisableWith = function () {
|
56
|
-
this.disabled = false
|
57
|
-
|
58
|
-
if (this.matches('button')) {
|
59
|
-
this.innerHTML = this.dataset.valueBeforeDisabled
|
60
|
-
} else {
|
61
|
-
this.value = this.dataset.valueBeforeDisabled
|
62
|
-
}
|
63
|
-
|
64
|
-
delete this.dataset.valueBeforeDisabled
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
}
|
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
class Middleware
|
5
|
-
# This middleware handles requests for assets in Rails engines. An engine that wants to expose
|
6
|
-
# its assets via Proscenium to the application must add itself to the list of engines in the
|
7
|
-
# Proscenium config options `Proscenium.config.engines`.
|
8
|
-
#
|
9
|
-
# For example, we have a gem that exposes a Rails engine.
|
10
|
-
#
|
11
|
-
# module Gem1
|
12
|
-
# class Engine < ::Rails::Engine
|
13
|
-
# config.proscenium.engines << self
|
14
|
-
# end
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# When this gem is installed in any Rails application, its assets will be available at the URL
|
18
|
-
# `/gem1/...`. For example, if the gem has a file `lib/styles.css`, it can be requested at
|
19
|
-
# `/gem1/lib/styles.css`.
|
20
|
-
#
|
21
|
-
class Engines < Esbuild
|
22
|
-
def real_path
|
23
|
-
@real_path ||= Pathname.new(@request.path.delete_prefix("/#{engine_name}")).to_s
|
24
|
-
end
|
25
|
-
|
26
|
-
def root_for_readable
|
27
|
-
ui? ? Proscenium.ui_path : engine.root
|
28
|
-
end
|
29
|
-
|
30
|
-
def engine
|
31
|
-
@engine ||= Proscenium.config.engines.find do |x|
|
32
|
-
@request.path.start_with?("/#{x.engine_name}")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def engine_name
|
37
|
-
ui? ? 'proscenium/ui' : engine.engine_name
|
38
|
-
end
|
39
|
-
|
40
|
-
def ui?
|
41
|
-
@request.path.start_with?('/proscenium/ui/')
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
class Middleware
|
5
|
-
class Runtime < Esbuild
|
6
|
-
private
|
7
|
-
|
8
|
-
def real_path
|
9
|
-
@real_path ||= Pathname.new(@request.path.sub(%r{^/@proscenium},
|
10
|
-
'/lib/proscenium/libs')).to_s
|
11
|
-
end
|
12
|
-
|
13
|
-
def root_for_readable
|
14
|
-
Proscenium::Railtie.root
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium::Phlex::AssetInclusions
|
4
|
-
def include_stylesheets
|
5
|
-
comment { '[PROSCENIUM_STYLESHEETS]' }
|
6
|
-
end
|
7
|
-
|
8
|
-
def include_javascripts
|
9
|
-
comment { '[PROSCENIUM_LAZY_SCRIPTS]' }
|
10
|
-
comment { '[PROSCENIUM_JAVASCRIPTS]' }
|
11
|
-
end
|
12
|
-
|
13
|
-
def include_assets
|
14
|
-
include_stylesheets
|
15
|
-
include_javascripts
|
16
|
-
end
|
17
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
module Phlex::CssModules
|
5
|
-
include Proscenium::CssModule
|
6
|
-
|
7
|
-
def self.included(base)
|
8
|
-
base.extend CssModule::Path
|
9
|
-
base.extend ClassMethods
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
# Set of CSS module paths that have been resolved after being transformed from 'class' HTML
|
14
|
-
# attributes. See #process_attributes. This is here because Phlex caches attributes. Which
|
15
|
-
# means while the CSS class names will be transformed, any resolved paths will be lost in
|
16
|
-
# subsequent requests.
|
17
|
-
attr_accessor :resolved_css_module_paths
|
18
|
-
end
|
19
|
-
|
20
|
-
def before_template
|
21
|
-
self.class.resolved_css_module_paths ||= Concurrent::Set.new
|
22
|
-
super
|
23
|
-
end
|
24
|
-
|
25
|
-
def after_template
|
26
|
-
self.class.resolved_css_module_paths.each do |path|
|
27
|
-
Proscenium::Importer.import path
|
28
|
-
end
|
29
|
-
|
30
|
-
super
|
31
|
-
end
|
32
|
-
|
33
|
-
# Resolve and side load any CSS modules in the "class" attributes, where a CSS module is a class
|
34
|
-
# name beginning with a `@`. The class name is resolved to a CSS module name based on the file
|
35
|
-
# system path of the Phlex class, and any CSS file is side loaded.
|
36
|
-
#
|
37
|
-
# For example, the following will side load the CSS module file at
|
38
|
-
# app/components/user/component.module.css, and add the CSS Module name `user_name` to the
|
39
|
-
# <div>.
|
40
|
-
#
|
41
|
-
# # app/components/user/component.rb
|
42
|
-
# class User::Component < Proscenium::Phlex
|
43
|
-
# def view_template
|
44
|
-
# div class: :@user_name do
|
45
|
-
# 'Joel Moss'
|
46
|
-
# end
|
47
|
-
# end
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# Additionally, any class name containing a `/` is resolved as a CSS module path. Allowing you
|
51
|
-
# to use the same syntax as a CSS module, but without the need to manually import the CSS file.
|
52
|
-
#
|
53
|
-
# For example, the following will side load the CSS module file at /lib/users.module.css, and
|
54
|
-
# add the CSS Module name `name` to the <div>.
|
55
|
-
#
|
56
|
-
# class User::Component < Proscenium::Phlex
|
57
|
-
# def view_template
|
58
|
-
# div class: '/lib/users@name' do
|
59
|
-
# 'Joel Moss'
|
60
|
-
# end
|
61
|
-
# end
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# @raise [Proscenium::CssModule::Resolver::NotFound] If a CSS module file is not found for the
|
65
|
-
# Phlex class file path.
|
66
|
-
def process_attributes(**attributes)
|
67
|
-
if attributes.key?(:class) && (attributes[:class] = tokens(attributes[:class])).include?('@')
|
68
|
-
names = attributes[:class].is_a?(Array) ? attributes[:class] : attributes[:class].split
|
69
|
-
|
70
|
-
attributes[:class] = cssm.class_names(*names).map do |name, path|
|
71
|
-
self.class.resolved_css_module_paths << path if path
|
72
|
-
name
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
attributes
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium
|
4
|
-
# Renders a <div> for use with React components, with data attributes specifying the component
|
5
|
-
# path and props.
|
6
|
-
#
|
7
|
-
# If a block is given, it will be yielded within the div, allowing for a custom "loading" UI. If
|
8
|
-
# no block is given, then a "loading..." text will be rendered. It is intended that the component
|
9
|
-
# is mounted to this div, and the loading UI will then be replaced with the component's rendered
|
10
|
-
# output.
|
11
|
-
#
|
12
|
-
# You can pass props to the component in the `:props` keyword argument.
|
13
|
-
class Phlex::ReactComponent < Phlex
|
14
|
-
self.abstract_class = true
|
15
|
-
|
16
|
-
include ReactComponentable
|
17
|
-
|
18
|
-
# Override this to provide your own loading UI.
|
19
|
-
#
|
20
|
-
# @example
|
21
|
-
# def view_template(**attributes, &block)
|
22
|
-
# super do
|
23
|
-
# 'Look at me! I am loading now...'
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# @yield the given block to a `div` within the top level component div.
|
28
|
-
def view_template(**attributes, &)
|
29
|
-
send(root_tag, **{ data: data_attributes }.deep_merge(attributes), &)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/proscenium/phlex.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'phlex-rails'
|
4
|
-
|
5
|
-
module Proscenium
|
6
|
-
class Phlex < ::Phlex::HTML
|
7
|
-
extend ActiveSupport::Autoload
|
8
|
-
|
9
|
-
autoload :CssModules
|
10
|
-
autoload :ReactComponent
|
11
|
-
autoload :AssetInclusions
|
12
|
-
|
13
|
-
include Proscenium::SourcePath
|
14
|
-
include CssModules
|
15
|
-
include AssetInclusions
|
16
|
-
|
17
|
-
module Sideload
|
18
|
-
def before_template
|
19
|
-
Proscenium::SideLoad.sideload_inheritance_chain self,
|
20
|
-
helpers.controller.sideload_assets_options
|
21
|
-
|
22
|
-
super
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class_attribute :sideload_assets_options
|
27
|
-
|
28
|
-
class << self
|
29
|
-
attr_accessor :abstract_class
|
30
|
-
|
31
|
-
def inherited(child)
|
32
|
-
child.prepend Sideload
|
33
|
-
|
34
|
-
super
|
35
|
-
end
|
36
|
-
|
37
|
-
def sideload_assets(value)
|
38
|
-
self.sideload_assets_options = value
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
@layer proscenium-ui-component {
|
2
|
-
/*
|
3
|
-
* Properties:
|
4
|
-
*
|
5
|
-
* --puiBreadcrumbs--link-color: LinkText;
|
6
|
-
* --puiBreadcrumbs--link-hover-color: HighlightText;
|
7
|
-
* --puiBreadcrumbs--separator-color: GrayText;
|
8
|
-
* --puiBreadcrumbs--separator: url("/proscenium/icons/angle-right-regular.svg");
|
9
|
-
*/
|
10
|
-
|
11
|
-
.base {
|
12
|
-
@mixin breadcrumbs from url("./mixins.css");
|
13
|
-
}
|
14
|
-
}
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'literal'
|
4
|
-
|
5
|
-
module Proscenium::UI
|
6
|
-
class Breadcrumbs::Component < Component
|
7
|
-
extend Literal::Properties
|
8
|
-
include Phlex::Rails::Helpers::URLFor
|
9
|
-
|
10
|
-
# The path (route) to use as the HREF for the home segment. Defaults to `:root`.
|
11
|
-
prop :home_path, _Union(String, Symbol), default: -> { :root }
|
12
|
-
|
13
|
-
# Assign false to hide the home segment.
|
14
|
-
prop :with_home, _Boolean, default: -> { true }
|
15
|
-
|
16
|
-
def view_template
|
17
|
-
div class: :@base do
|
18
|
-
ol do
|
19
|
-
if @with_home
|
20
|
-
li do
|
21
|
-
home_template
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
breadcrumbs.each do |ce|
|
26
|
-
li do
|
27
|
-
path = ce.path
|
28
|
-
path.nil? ? ce.name : a(href: url_for(path)) { ce.name }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
# Override this to customise the home breadcrumb. You can call super with a block to use the
|
38
|
-
# default template, but with custom content.
|
39
|
-
#
|
40
|
-
# @example
|
41
|
-
# def home_template
|
42
|
-
# super { 'hello' }
|
43
|
-
# end
|
44
|
-
def home_template(&block)
|
45
|
-
a(href: url_for(@home_path)) do
|
46
|
-
if block
|
47
|
-
yield
|
48
|
-
else
|
49
|
-
svg role: 'img', xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 576 512' do |s|
|
50
|
-
s.path fill: 'currentColor',
|
51
|
-
d: 'M488 312.7V456c0 13.3-10.7 24-24 24H348c-6.6 0-12-5.4-12-12V356c0-6.6-5.4-' \
|
52
|
-
'12-12-12h-72c-6.6 0-12 5.4-12 12v112c0 6.6-5.4 12-12 12H112c-13.3 0-24-10.' \
|
53
|
-
'7-24-24V312.7c0-3.6 1.6-7 4.4-9.3l188-154.8c4.4-3.6 10.8-3.6 15.3 0l188 15' \
|
54
|
-
'4.8c2.7 2.3 4.3 5.7 4.3 9.3zm83.6-60.9L488 182.9V44.4c0-6.6-5.4-12-12-12h-' \
|
55
|
-
'56c-6.6 0-12 5.4-12 12V117l-89.5-73.7c-17.7-14.6-43.3-14.6-61 0L4.4 251.8c' \
|
56
|
-
'-5.1 4.2-5.8 11.8-1.6 16.9l25.5 31c4.2 5.1 11.8 5.8 16.9 1.6l235.2-193.7c4' \
|
57
|
-
'.4-3.6 10.8-3.6 15.3 0l235.2 193.7c5.1 4.2 12.7 3.5 16.9-1.6l25.5-31c4.2-5' \
|
58
|
-
'.2 3.4-12.7-1.7-16.9z'
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Don't render if @hide_breadcrumbs is true.
|
65
|
-
def render?
|
66
|
-
helpers.assigns['hide_breadcrumbs'] != true
|
67
|
-
end
|
68
|
-
|
69
|
-
def breadcrumbs
|
70
|
-
helpers.controller.breadcrumbs.map { |e| Breadcrumbs::ComputedElement.new e, helpers }
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Proscenium::UI::Breadcrumbs
|
4
|
-
class ComputedElement
|
5
|
-
def initialize(element, context)
|
6
|
-
@element = element
|
7
|
-
@context = context
|
8
|
-
end
|
9
|
-
|
10
|
-
# If name is a Symbol of a controller method, that method is called.
|
11
|
-
# If name is a Symbol of a controller instance variable, that variable is returned.
|
12
|
-
# If name is a Proc, it is executed in the context of the controller instance.
|
13
|
-
#
|
14
|
-
# @return [String] the content of the breadcrumb element.
|
15
|
-
def name
|
16
|
-
@name ||= case name = @element.name
|
17
|
-
when Symbol
|
18
|
-
if name.to_s.starts_with?('@')
|
19
|
-
name = get_instance_variable(name)
|
20
|
-
name.respond_to?(:for_breadcrumb) ? name.for_breadcrumb : name.to_s
|
21
|
-
else
|
22
|
-
res = @context.controller.send(name)
|
23
|
-
res.try(:for_breadcrumb) || res.to_s
|
24
|
-
end
|
25
|
-
when Proc
|
26
|
-
@context.controller.instance_exec(&name)
|
27
|
-
else
|
28
|
-
name.respond_to?(:for_breadcrumb) ? name.for_breadcrumb : name.to_s
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# If path is a Symbol of a controller method, that method is called.
|
33
|
-
# If path is a Symbol of a controller instance variable, that variable is returned.
|
34
|
-
# If path is an Array, each element is processed as above.
|
35
|
-
# If path is a Proc, it is executed in the context of the controller instance.
|
36
|
-
#
|
37
|
-
# No matter what, the result is always passed to `url_for` before being returned.
|
38
|
-
#
|
39
|
-
# @return [String] the URL for the element
|
40
|
-
def path
|
41
|
-
@path ||= unless @element.path.nil?
|
42
|
-
case path = @element.path
|
43
|
-
when Array
|
44
|
-
path.map! { |x| x.to_s.starts_with?('@') ? get_instance_variable(x) : x }
|
45
|
-
when Symbol
|
46
|
-
if path.to_s.starts_with?('@')
|
47
|
-
path = get_instance_variable(path)
|
48
|
-
elsif @context.controller.respond_to?(path, true)
|
49
|
-
path = @context.controller.send(path)
|
50
|
-
end
|
51
|
-
when Proc
|
52
|
-
path = @context.controller.instance_exec(&path)
|
53
|
-
end
|
54
|
-
|
55
|
-
@context.url_for path
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def get_instance_variable(element)
|
62
|
-
unless @context.instance_variable_defined?(element)
|
63
|
-
raise NameError, "undefined instance variable `#{element}' for breadcrumb", caller
|
64
|
-
end
|
65
|
-
|
66
|
-
@context.instance_variable_get element
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module 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 filter_options [Hash] Options to pass through to the before_action filter.
|
22
|
-
def add_breadcrumb(name, path = nil, **filter_options)
|
23
|
-
element_options = filter_options.delete(:options) || {}
|
24
|
-
|
25
|
-
before_action(filter_options) do |controller|
|
26
|
-
controller.send :add_breadcrumb, name, path, element_options
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Prepend a new breadcrumb element into the collection.
|
31
|
-
#
|
32
|
-
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
33
|
-
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
34
|
-
# breadcrumb.
|
35
|
-
# @param filter_options [Hash] Options to pass through to the before_action filter.
|
36
|
-
def prepend_breadcrumb(name, path = nil, **filter_options)
|
37
|
-
element_options = filter_options.delete(:options) || {}
|
38
|
-
|
39
|
-
before_action(filter_options) do |controller|
|
40
|
-
controller.send :prepend_breadcrumb, name, path, element_options
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Pushes a new breadcrumb element into the collection.
|
46
|
-
#
|
47
|
-
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
48
|
-
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
49
|
-
# breadcrumb.
|
50
|
-
# @param options [Hash]
|
51
|
-
def add_breadcrumb(name, path = nil, options = {})
|
52
|
-
breadcrumbs << Element.new(name, path, options)
|
53
|
-
end
|
54
|
-
|
55
|
-
# Prepend a new breadcrumb element into the collection.
|
56
|
-
#
|
57
|
-
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
58
|
-
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
59
|
-
# breadcrumb.
|
60
|
-
# @param options [Hash]
|
61
|
-
def prepend_breadcrumb(name, path = nil, options = {})
|
62
|
-
breadcrumbs.prepend Element.new(name, path, options)
|
63
|
-
end
|
64
|
-
|
65
|
-
def breadcrumbs
|
66
|
-
@breadcrumbs ||= []
|
67
|
-
end
|
68
|
-
|
69
|
-
def breadcrumbs_as_json
|
70
|
-
computed_breadcrumbs.map do |ele|
|
71
|
-
path = ele.path
|
72
|
-
|
73
|
-
{ name: ele.name, path: ele.path.nil? || helpers.current_page?(path) ? nil : path }
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# @param primary [Boolean] whether to return only the primary breadcrumb.
|
78
|
-
def breadcrumbs_for_title(primary: false)
|
79
|
-
names = computed_breadcrumbs.map(&:name)
|
80
|
-
return names.pop if primary
|
81
|
-
|
82
|
-
out = [names.pop]
|
83
|
-
out << names.join(': ') unless names.empty?
|
84
|
-
strip_tags out.join(' - ')
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def computed_breadcrumbs
|
90
|
-
@computed_breadcrumbs ||= breadcrumbs.map do |ele|
|
91
|
-
ComputedElement.new ele, helpers
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
@define-mixin breadcrumbs {
|
2
|
-
/* Default properties */
|
3
|
-
--_puiBreadcrumbs--separator-color: GrayText;
|
4
|
-
--_puiBreadcrumbs--separator: url("/proscenium/icons/angle-right-regular.svg");
|
5
|
-
|
6
|
-
margin: 10px;
|
7
|
-
|
8
|
-
ol {
|
9
|
-
list-style: none;
|
10
|
-
padding: 0;
|
11
|
-
margin: 0;
|
12
|
-
display: flex;
|
13
|
-
align-items: baseline;
|
14
|
-
|
15
|
-
li {
|
16
|
-
text-transform: uppercase;
|
17
|
-
display: flex;
|
18
|
-
align-items: center;
|
19
|
-
|
20
|
-
@media (max-width: 426px) {
|
21
|
-
&:not(:nth-last-child(2)) {
|
22
|
-
display: none;
|
23
|
-
}
|
24
|
-
|
25
|
-
&:nth-last-child(2)::before {
|
26
|
-
@mixin _separator;
|
27
|
-
margin: 0 0.5rem 0 0;
|
28
|
-
transform: rotate(180deg);
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
@media (min-width: 427px) {
|
33
|
-
&:not(:last-child)::after {
|
34
|
-
@mixin _separator;
|
35
|
-
margin: 0 0.5rem;
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
&:last-child {
|
40
|
-
font-weight: 500;
|
41
|
-
text-transform: none;
|
42
|
-
}
|
43
|
-
|
44
|
-
&:last-child > a {
|
45
|
-
font-weight: 500;
|
46
|
-
text-transform: none;
|
47
|
-
}
|
48
|
-
|
49
|
-
a {
|
50
|
-
color: var(--puiBreadcrumbs--link-color, revert);
|
51
|
-
display: flex;
|
52
|
-
|
53
|
-
&:hover {
|
54
|
-
color: var(--puiBreadcrumbs--link-hover-color, revert);
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
svg {
|
59
|
-
height: 1em;
|
60
|
-
width: 1em;
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
@define-mixin _separator {
|
67
|
-
display: inline-block;
|
68
|
-
content: "";
|
69
|
-
height: 1rem;
|
70
|
-
width: 1rem;
|
71
|
-
-webkit-mask: var(
|
72
|
-
--puiBreadcrumbs--separator,
|
73
|
-
var(--_puiBreadcrumbs--separator)
|
74
|
-
)
|
75
|
-
no-repeat center center;
|
76
|
-
mask: var(--puiBreadcrumbs--separator, var(--_puiBreadcrumbs--separator))
|
77
|
-
no-repeat center center;
|
78
|
-
vertical-align: sub;
|
79
|
-
background-color: var(
|
80
|
-
--puiBreadcrumbs--separator-color,
|
81
|
-
var(--_puiBreadcrumbs--separator-color)
|
82
|
-
);
|
83
|
-
}
|