proscenium 0.19.0.beta15-arm64-darwin → 0.19.0.test1-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 +44 -0
- data/lib/proscenium/bundled_gems.rb +1 -1
- data/lib/proscenium/css_module.rb +5 -0
- data/lib/proscenium/middleware.rb +6 -0
- data/lib/proscenium/monkey.rb +9 -2
- data/lib/proscenium/phlex.rb +6 -0
- data/lib/proscenium/railtie.rb +22 -2
- data/lib/proscenium/react_componentable.rb +1 -1
- data/lib/proscenium/registry/bundled_package.rb +29 -0
- data/lib/proscenium/registry/package.rb +95 -0
- data/lib/proscenium/registry/ruby_gem_package.rb +30 -0
- data/lib/proscenium/registry.rb +29 -0
- data/lib/proscenium/resolver.rb +9 -0
- data/lib/proscenium/ruby_gems.rb +67 -0
- data/lib/proscenium/ui/breadcrumbs/component.module.css +14 -0
- data/lib/proscenium/ui/breadcrumbs/component.rb +71 -0
- data/lib/proscenium/ui/breadcrumbs/computed_element.rb +69 -0
- data/lib/proscenium/ui/breadcrumbs/control.rb +95 -0
- data/lib/proscenium/ui/breadcrumbs/mixins.css +83 -0
- data/lib/proscenium/ui/breadcrumbs.rb +72 -0
- data/lib/proscenium/ui/component.rb +7 -0
- data/lib/proscenium/ui/custom_element.js +54 -0
- data/lib/proscenium/ui/flash/bun.lock +19 -0
- data/lib/proscenium/ui/flash/index.css +1 -0
- data/lib/proscenium/ui/flash/index.js +77 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.d.ts +33 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.js +44 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/license +9 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/package.json +59 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/readme.md +125 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/LICENSE +20 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/README.md +11 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/package.json +44 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.css +697 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.js +537 -0
- data/lib/proscenium/ui/flash/package.json +11 -0
- data/lib/proscenium/ui/flash.rb +15 -0
- data/lib/proscenium/ui/form/field_methods.rb +88 -0
- data/lib/proscenium/ui/form/fields/base.rb +188 -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/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 +173 -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 +300 -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/translation.rb +71 -0
- data/lib/proscenium/ui/form.css +52 -0
- data/lib/proscenium/ui/form.rb +211 -0
- data/lib/proscenium/ui/props.css +7 -0
- data/lib/proscenium/ui/stimulus-loading.js +65 -0
- data/lib/proscenium/ui/test.js +1 -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 +9 -0
- data/lib/proscenium/ui.rb +11 -0
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component/css_modules.rb +11 -0
- data/lib/proscenium/view_component/react_component.rb +22 -0
- data/lib/proscenium/view_component/sideload.rb +4 -0
- data/lib/proscenium/view_component.rb +36 -0
- data/lib/proscenium.rb +34 -6
- metadata +136 -24
- /data/lib/proscenium/{react-manager → ui/react-manager}/index.jsx +0 -0
- /data/lib/proscenium/{react-manager → ui/react-manager}/react.js +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 922316663697224b4854d865338644b3353946efbb4e05007d7c41a083cee8f1
|
4
|
+
data.tar.gz: 431cd3e4d140afc3ff6e37ed8cbb9acb908891567b3787e3926ea45d969a29c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf4f31a7f222382db2df7a4ca5572d0ea9439689129ce27883765440c5b69c6484a80d4256279738f89c5598907601e7b1bcbd69d7d508d8725863edc7c95424
|
7
|
+
data.tar.gz: 47b5506862294c1089de8fad7cadbc8f7bc4b081bc4ec537100003e6cb6615e88c886c39dc8f5761e67452976fef88b9e4a57fb54a918e3b10dcca782aadf481
|
data/README.md
CHANGED
@@ -49,6 +49,7 @@
|
|
49
49
|
- [JSX](#jsx)
|
50
50
|
- [JSON](#json)
|
51
51
|
- [Phlex Support](#phlex-support)
|
52
|
+
- [ViewComponent Support](#viewcomponent-support)
|
52
53
|
- [Cache Busting](#cache-busting)
|
53
54
|
- [rjs is back!](#rjs-is-back)
|
54
55
|
- [Resolution](#resolution)
|
@@ -768,6 +769,49 @@ end
|
|
768
769
|
<h1 class="user_name-ABCD1234 title">Joel Moss</h1>
|
769
770
|
```
|
770
771
|
|
772
|
+
## ViewComponent Support
|
773
|
+
|
774
|
+
[ViewComponent](https://viewcomponent.org/) iA framework for creating reusable, testable & encapsulated view components, built to integrate seamlessly with Ruby on Rails. Proscenium works perfectly with ViewComponent, with support for side-loading, CSS modules, and more. Simply write your ViewComponent classes and inherit from `Proscenium::ViewComponent`.
|
775
|
+
|
776
|
+
```ruby
|
777
|
+
class MyView < Proscenium::ViewComponent
|
778
|
+
def call
|
779
|
+
tag.h1 'Hello World'
|
780
|
+
end
|
781
|
+
end
|
782
|
+
```
|
783
|
+
|
784
|
+
### Side-loading
|
785
|
+
|
786
|
+
Any ViewComponent class that inherits `Proscenium::ViewComponent` will automatically be [side-loaded](#side-loading).
|
787
|
+
|
788
|
+
### CSS Modules
|
789
|
+
|
790
|
+
[CSS Modules](#css-modules) are fully supported in ViewComponent classes, with access to the [`css_module` helper](#in-your-views) if you need it.
|
791
|
+
|
792
|
+
```ruby
|
793
|
+
# /app/components/user_component.rb
|
794
|
+
class UserComponent < Proscenium::ViewComponent
|
795
|
+
def view_template
|
796
|
+
div.h1 @user.name, class: css_module(:user_name)
|
797
|
+
end
|
798
|
+
end
|
799
|
+
```
|
800
|
+
|
801
|
+
```css
|
802
|
+
/* # /app/components/user_component.module.css */
|
803
|
+
.userName {
|
804
|
+
color: red;
|
805
|
+
font-size: 50px;
|
806
|
+
}
|
807
|
+
```
|
808
|
+
|
809
|
+
The view above will be rendered something like this:
|
810
|
+
|
811
|
+
```html
|
812
|
+
<h1 class="user_name-ABCD1234">Joel Moss</h1>
|
813
|
+
```
|
814
|
+
|
771
815
|
## Cache Busting
|
772
816
|
|
773
817
|
> _COMING SOON_
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Proscenium::CssModule
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :Path
|
7
|
+
autoload :Transformer
|
8
|
+
|
4
9
|
class TransformError < StandardError
|
5
10
|
def initialize(name, additional_msg = nil)
|
6
11
|
msg = "Failed to transform CSS module `#{name}`"
|
@@ -2,9 +2,15 @@
|
|
2
2
|
|
3
3
|
module Proscenium
|
4
4
|
class Middleware
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
5
7
|
# Error when the build command fails.
|
6
8
|
class BuildError < StandardError; end
|
7
9
|
|
10
|
+
autoload :Base
|
11
|
+
autoload :Esbuild
|
12
|
+
autoload :RubyGems
|
13
|
+
|
8
14
|
def initialize(app)
|
9
15
|
@app = app
|
10
16
|
|
data/lib/proscenium/monkey.rb
CHANGED
@@ -9,8 +9,15 @@ module Proscenium
|
|
9
9
|
result = super
|
10
10
|
return result if !view.controller || !Proscenium.config.side_load
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
renderable = template.instance_variable_get(:@renderable)
|
13
|
+
|
14
|
+
to_sideload = if Object.const_defined?(:ViewComponent) &&
|
15
|
+
template.is_a?(ActionView::Template::Renderable) &&
|
16
|
+
renderable.class < ::ViewComponent::Base &&
|
17
|
+
renderable.class.format == :html
|
18
|
+
renderable
|
19
|
+
elsif template.respond_to?(:virtual_path) &&
|
20
|
+
template.respond_to?(:type) && template.type == :html
|
14
21
|
template
|
15
22
|
end
|
16
23
|
if to_sideload
|
data/lib/proscenium/phlex.rb
CHANGED
@@ -4,6 +4,12 @@ require 'phlex-rails'
|
|
4
4
|
|
5
5
|
module Proscenium
|
6
6
|
class Phlex < ::Phlex::HTML
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
autoload :CssModules
|
10
|
+
autoload :ReactComponent
|
11
|
+
autoload :AssetInclusions
|
12
|
+
|
7
13
|
include Proscenium::SourcePath
|
8
14
|
include CssModules
|
9
15
|
include AssetInclusions
|
data/lib/proscenium/railtie.rb
CHANGED
@@ -14,7 +14,9 @@ module Proscenium
|
|
14
14
|
config.proscenium.bundle = true
|
15
15
|
config.proscenium.side_load = true
|
16
16
|
config.proscenium.code_splitting = true
|
17
|
+
|
17
18
|
config.proscenium.ensure_loaded = :raise
|
19
|
+
|
18
20
|
config.proscenium.cache_query_string = Rails.env.production? && ENV.fetch('REVISION', nil)
|
19
21
|
config.proscenium.cache_max_age = 2_592_000 # 30 days
|
20
22
|
|
@@ -33,9 +35,17 @@ module Proscenium
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
38
|
+
initializer 'proscenium.ui' do
|
39
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
40
|
+
inflect.acronym 'UI'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
36
44
|
initializer 'proscenium.debugging' do
|
37
|
-
|
38
|
-
|
45
|
+
if Rails.gem_version >= Gem::Version.new('7.1.0')
|
46
|
+
tpl_path = root.join('lib', 'proscenium', 'templates').to_s
|
47
|
+
ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS << tpl_path
|
48
|
+
end
|
39
49
|
end
|
40
50
|
|
41
51
|
initializer 'proscenium.middleware' do |app|
|
@@ -55,5 +65,15 @@ module Proscenium
|
|
55
65
|
ActionView::PartialRenderer.prepend Monkey::PartialRenderer
|
56
66
|
end
|
57
67
|
end
|
68
|
+
|
69
|
+
initializer 'proscenium.public_path' do |app|
|
70
|
+
if app.config.public_file_server.enabled
|
71
|
+
headers = app.config.public_file_server.headers || {}
|
72
|
+
index = app.config.public_file_server.index_name || 'index'
|
73
|
+
|
74
|
+
app.middleware.insert_after(ActionDispatch::Static, ActionDispatch::Static,
|
75
|
+
root.join('public').to_s, index:, headers:)
|
76
|
+
end
|
77
|
+
end
|
58
78
|
end
|
59
79
|
end
|
@@ -11,7 +11,7 @@ module Proscenium
|
|
11
11
|
# The HTML tag to use as the wrapping element for the component. You can reassign this in your
|
12
12
|
# component class to use a different tag:
|
13
13
|
#
|
14
|
-
# class MyComponent < Proscenium::
|
14
|
+
# class MyComponent < Proscenium::ViewComponent::ReactComponent
|
15
15
|
# self.root_tag = :span
|
16
16
|
# end
|
17
17
|
#
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems/package'
|
4
|
+
|
5
|
+
class Proscenium::Registry
|
6
|
+
class BundledPackage < Package
|
7
|
+
def version = @version ||= spec.version.to_s
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def package_json
|
12
|
+
@package_json ||= begin
|
13
|
+
unless (gem_path = Proscenium::BundledGems.pathname_for(gem_name))
|
14
|
+
raise PackageNotInstalledError, name
|
15
|
+
end
|
16
|
+
|
17
|
+
if (package_path = gem_path.join('package.json')).exist?
|
18
|
+
JSON.parse(package_path.read)
|
19
|
+
else
|
20
|
+
default_package_json
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def spec
|
26
|
+
@spec ||= Bundler.load.specs[gem_name].first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Proscenium::Registry
|
4
|
+
# 1. Fetch the gem metadata from RubyGems API.
|
5
|
+
# 2. Extract any package.json from the gem, and populate the response with it.
|
6
|
+
# 3. Create a tarball containing the fetched package.json. This will be downloaded by the npm
|
7
|
+
# client, and unpacked into node_modules. Proscenium ignores this, as it will pull contents
|
8
|
+
# directly from location of the installed gem.
|
9
|
+
# 4. Return a valid npm response listing package details, tarball location, and its dependencies.
|
10
|
+
#
|
11
|
+
# See https://wiki.commonjs.org/wiki/Packages/Registry
|
12
|
+
class Package
|
13
|
+
extend Literal::Properties
|
14
|
+
|
15
|
+
prop :name, String, :positional, reader: :private
|
16
|
+
prop :version, _String?
|
17
|
+
prop :host, String
|
18
|
+
|
19
|
+
def as_json
|
20
|
+
{
|
21
|
+
name:,
|
22
|
+
'dist-tags': {
|
23
|
+
latest: version
|
24
|
+
},
|
25
|
+
versions: {
|
26
|
+
version => {
|
27
|
+
name:,
|
28
|
+
version:,
|
29
|
+
dependencies: package_json['dependencies'] || {},
|
30
|
+
dist: {
|
31
|
+
tarball:,
|
32
|
+
integrity:,
|
33
|
+
shasum:
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate!
|
41
|
+
return self if name.start_with?('@rubygems/')
|
42
|
+
|
43
|
+
raise PackageUnsupportedError, name
|
44
|
+
end
|
45
|
+
|
46
|
+
def gem_name = @gem_name ||= name.gsub('@rubygems/', '')
|
47
|
+
def version = @version # rubocop:disable Style/TrivialAccessors
|
48
|
+
def shasum = Digest::SHA1.file(tarball_path).hexdigest
|
49
|
+
def integrity = "sha512-#{Digest::SHA512.file(tarball_path).base64digest}"
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def tarball
|
54
|
+
create_tarball unless tarball_path.exist?
|
55
|
+
|
56
|
+
"#{@host}/#{tarball_path.relative_path_from(Rails.public_path)}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def tarball_name
|
60
|
+
@tarball_name ||= "#{gem_name}-#{version}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def tarball_path
|
64
|
+
@tarball_path ||= Rails.public_path.join('proscenium_registry_tarballs')
|
65
|
+
.join("@rubygems/#{gem_name}/#{tarball_name}.tgz")
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_tarball
|
69
|
+
FileUtils.mkdir_p(File.dirname(tarball_path))
|
70
|
+
|
71
|
+
File.open(tarball_path, 'wb') do |file|
|
72
|
+
Zlib::GzipWriter.wrap(file) do |gz|
|
73
|
+
Gem::Package::TarWriter.new(gz) do |tar|
|
74
|
+
contents = package_json.to_json
|
75
|
+
tar.add_file_simple('package/package.json', 0o444, contents.length) do |io|
|
76
|
+
io.write contents
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def package_json
|
84
|
+
@package_json ||= default_package_json
|
85
|
+
end
|
86
|
+
|
87
|
+
def default_package_json
|
88
|
+
{
|
89
|
+
name:,
|
90
|
+
version:,
|
91
|
+
dependencies: {}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gems'
|
4
|
+
|
5
|
+
class Proscenium::Registry
|
6
|
+
class RubyGemPackage < Package
|
7
|
+
def version = spec['version']
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def package_json
|
12
|
+
@package_json ||= begin
|
13
|
+
package_path = Proscenium::RubyGems.path_for(gem_name, version).join('package.json')
|
14
|
+
if package_path.exist?
|
15
|
+
JSON.parse path.read
|
16
|
+
else
|
17
|
+
default_package_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def spec
|
23
|
+
@spec ||= if @version.present?
|
24
|
+
Gems::V2.info gem_name, @version
|
25
|
+
else
|
26
|
+
Gems.info(gem_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Proscenium::Registry
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :Package
|
7
|
+
autoload :BundledPackage
|
8
|
+
autoload :RubyGemPackage
|
9
|
+
|
10
|
+
class PackageUnsupportedError < StandardError
|
11
|
+
def initialize(name)
|
12
|
+
super("Package `#{name}` is not valid; only Ruby gems are supported via the @rubygems scope.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class PackageNotInstalledError < StandardError
|
17
|
+
def initialize(name)
|
18
|
+
super("Package `#{name}` is not found in your bundle; have you installed the Ruby gem?")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.bundled_package(name, host:)
|
23
|
+
BundledPackage.new(name, host:).validate!
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.ruby_gem_package(name, version, host:)
|
27
|
+
RubyGemPackage.new(name, version:, host:).validate!
|
28
|
+
end
|
29
|
+
end
|
data/lib/proscenium/resolver.rb
CHANGED
@@ -19,7 +19,16 @@ module Proscenium
|
|
19
19
|
end
|
20
20
|
|
21
21
|
self.resolved[path] ||= if (gem = BundledGems.paths.find { |_, v| path.start_with? "#{v}/" })
|
22
|
+
# If the path is a rubygem, and it is installed with npm via the
|
23
|
+
# @rubygems scope, then resolve the path to the symlinked location
|
24
|
+
# in node_modules.
|
25
|
+
# npm_path = Rails.root.join("node_modules/@rubygems/#{gem.first}")
|
26
|
+
# if npm_path.symlink?
|
27
|
+
# npm_path.realpath.join(path.sub(/^#{gem.last}/, '.')).to_s
|
28
|
+
# .delete_prefix(Rails.root.to_s)
|
29
|
+
# else
|
22
30
|
path.sub(/^#{gem.last}/, "/node_modules/@rubygems/#{gem.first}")
|
31
|
+
# end
|
23
32
|
elsif path.start_with?("#{Rails.root}/")
|
24
33
|
path.delete_prefix Rails.root.to_s
|
25
34
|
else
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems/package'
|
4
|
+
require 'rubygems/remote_fetcher'
|
5
|
+
|
6
|
+
module Proscenium
|
7
|
+
class RubyGems
|
8
|
+
def self.path_for(name, version = nil)
|
9
|
+
Pathname new(name, version).path
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name, version = nil)
|
13
|
+
@name = name
|
14
|
+
@version = version
|
15
|
+
end
|
16
|
+
|
17
|
+
def path
|
18
|
+
dependency = Gem::Dependency.new @name, @version
|
19
|
+
path = gem_path dependency
|
20
|
+
|
21
|
+
raise "Gem '#{@name}' not installed nor fetchable." unless path
|
22
|
+
|
23
|
+
basename = File.basename path, '.gem'
|
24
|
+
target_dir = File.expand_path basename, Rails.root.join('tmp', 'unpacked_gems')
|
25
|
+
|
26
|
+
Gem::Package.new(path).extract_files target_dir
|
27
|
+
|
28
|
+
target_dir
|
29
|
+
end
|
30
|
+
|
31
|
+
# Find cached filename in Gem.path. Returns nil if the file cannot be found.
|
32
|
+
def find_in_cache(filename)
|
33
|
+
Gem.path.each do |path|
|
34
|
+
this_path = File.join(path, 'cache', filename)
|
35
|
+
return this_path if File.exist? this_path
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return the full path to the cached gem file matching the given
|
42
|
+
# name and version requirement. Returns 'nil' if no match.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# get_path 'rake', '> 0.4' # "/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem"
|
47
|
+
# get_path 'rake', '< 0.1' # nil
|
48
|
+
# get_path 'rak' # nil (exact name required)
|
49
|
+
def gem_path(dependency)
|
50
|
+
return dependency.name if /\.gem$/i.match?(dependency.name)
|
51
|
+
|
52
|
+
specs = dependency.matching_specs
|
53
|
+
selected = specs.max_by(&:version)
|
54
|
+
|
55
|
+
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless selected
|
56
|
+
return unless /^#{selected.name}$/i.match?(dependency.name)
|
57
|
+
|
58
|
+
# We expect to find (basename).gem in the 'cache' directory. Furthermore,
|
59
|
+
# the name match must be exact (ignoring case).
|
60
|
+
path = find_in_cache File.basename selected.cache_file
|
61
|
+
|
62
|
+
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path
|
63
|
+
|
64
|
+
path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,14 @@
|
|
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
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Proscenium::UI
|
4
|
+
class Breadcrumbs::Component < Component
|
5
|
+
extend Literal::Properties
|
6
|
+
include Phlex::Rails::Helpers::URLFor
|
7
|
+
|
8
|
+
# The path (route) to use as the HREF for the home segment. Defaults to `:root`.
|
9
|
+
prop :home_path, _Union(String, Symbol), default: -> { :root }
|
10
|
+
|
11
|
+
# Assign false to hide the home segment.
|
12
|
+
prop :with_home, _Boolean, default: -> { true }
|
13
|
+
|
14
|
+
def view_template
|
15
|
+
div class: :@base do
|
16
|
+
ol do
|
17
|
+
if @with_home
|
18
|
+
li do
|
19
|
+
home_template
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
breadcrumbs.each do |ce|
|
24
|
+
li do
|
25
|
+
path = ce.path
|
26
|
+
path.nil? ? ce.name : a(href: url_for(path)) { ce.name }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Override this to customise the home breadcrumb. You can call super with a block to use the
|
36
|
+
# default template, but with custom content.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# def home_template
|
40
|
+
# super { 'hello' }
|
41
|
+
# end
|
42
|
+
def home_template(&block)
|
43
|
+
a(href: url_for(@home_path)) do
|
44
|
+
if block
|
45
|
+
yield
|
46
|
+
else
|
47
|
+
svg role: 'img', xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 576 512' do |s|
|
48
|
+
s.path fill: 'currentColor',
|
49
|
+
d: 'M488 312.7V456c0 13.3-10.7 24-24 24H348c-6.6 0-12-5.4-12-12V356c0-6.6-5.4-' \
|
50
|
+
'12-12-12h-72c-6.6 0-12 5.4-12 12v112c0 6.6-5.4 12-12 12H112c-13.3 0-24-10.' \
|
51
|
+
'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' \
|
52
|
+
'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-' \
|
53
|
+
'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' \
|
54
|
+
'-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' \
|
55
|
+
'.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' \
|
56
|
+
'.2 3.4-12.7-1.7-16.9z'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Don't render if @hide_breadcrumbs is true.
|
63
|
+
def render?
|
64
|
+
helpers.assigns['hide_breadcrumbs'] != true
|
65
|
+
end
|
66
|
+
|
67
|
+
def breadcrumbs
|
68
|
+
helpers.controller.breadcrumbs.map { |e| Breadcrumbs::ComputedElement.new e, helpers }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,69 @@
|
|
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
|