phlex 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of phlex might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +4 -0
- data/Gemfile +15 -3
- data/README.md +14 -30
- data/Rakefile +4 -6
- data/bench.rb +14 -0
- data/config.ru +9 -0
- data/docs/assets/application.css +24 -0
- data/docs/assets/logo.png +0 -0
- data/docs/build.rb +22 -0
- data/docs/components/callout.rb +9 -0
- data/docs/components/code_block.rb +26 -0
- data/docs/components/example.rb +32 -0
- data/docs/components/heading.rb +9 -0
- data/docs/components/layout.rb +45 -0
- data/docs/components/markdown.rb +26 -0
- data/docs/components/tabs/tab.rb +26 -0
- data/docs/components/tabs.rb +30 -0
- data/docs/components/title.rb +9 -0
- data/docs/page_builder.rb +36 -0
- data/docs/pages/application_page.rb +7 -0
- data/docs/pages/components.rb +175 -0
- data/docs/pages/index.rb +40 -0
- data/docs/pages/templates.rb +242 -0
- data/fixtures/component_helper.rb +16 -0
- data/fixtures/dummy/app/assets/config/manifest.js +0 -0
- data/fixtures/dummy/app/controllers/articles_controller.rb +4 -0
- data/fixtures/dummy/app/views/articles/form.rb +13 -0
- data/fixtures/dummy/app/views/articles/index.html.erb +11 -0
- data/fixtures/dummy/app/views/articles/new.html.erb +1 -0
- data/fixtures/dummy/app/views/card.rb +13 -0
- data/fixtures/dummy/config/database.yml +3 -0
- data/fixtures/dummy/config/routes.rb +5 -0
- data/fixtures/dummy/config/storage.yml +3 -0
- data/fixtures/dummy/db/schema.rb +6 -0
- data/fixtures/dummy/log/.gitignore +1 -0
- data/fixtures/dummy/public/favicon.ico +0 -0
- data/fixtures/layout.rb +31 -0
- data/fixtures/page.rb +41 -0
- data/fixtures/test_helper.rb +13 -0
- data/lib/generators/phlex/component/USAGE +8 -0
- data/lib/generators/phlex/component/component_generator.rb +13 -0
- data/lib/generators/phlex/component/templates/component.rb.erb +8 -0
- data/lib/overrides/symbol/name.rb +5 -0
- data/lib/phlex/block.rb +18 -0
- data/lib/phlex/buffered.rb +19 -0
- data/lib/phlex/component.rb +169 -23
- data/lib/phlex/configuration.rb +7 -0
- data/lib/phlex/html.rb +65 -0
- data/lib/phlex/rails/tag_helpers.rb +29 -0
- data/lib/phlex/rails.rb +8 -0
- data/lib/phlex/renderable.rb +35 -0
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +25 -15
- data/package-lock.json +1195 -0
- data/package.json +5 -0
- data/phlex_logo.png +0 -0
- data/tailwind.config.js +7 -0
- metadata +64 -21
- data/Gemfile.lock +0 -32
- data/lib/phlex/callable.rb +0 -11
- data/lib/phlex/context.rb +0 -39
- data/lib/phlex/node.rb +0 -13
- data/lib/phlex/page.rb +0 -3
- data/lib/phlex/tag/standard_element.rb +0 -15
- data/lib/phlex/tag/void_element.rb +0 -9
- data/lib/phlex/tag.rb +0 -43
- data/lib/phlex/tags.rb +0 -108
- data/lib/phlex/text.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d97833c25f39a4fb198d625faa17bb4d98cf722fbf4f06ae5e4bd2af80d8f4aa
|
4
|
+
data.tar.gz: ccdade6eb46b4d43cd49783f1557292c62757c4073f3d31a0a57bbccee7b2ef8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19e991153278324c20648922bc076823f702f80d65cc3459221acfa791696825f405d5b1d31d059e9846050dfec2591547943007cbcaf4ed76123cd00f07f3e1
|
7
|
+
data.tar.gz: b3e43d5a68cf93070b49232475d27c7f45ef303cf3c1b1e8fd6ebe5afe6f44dfb3b3548418326ddaafb890662e9ee71e1a0aabb160be1e4ca65c73b6252ce8cf
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
4
5
|
|
5
|
-
# Specify your gem's dependencies in phlex.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
gem "rake"
|
8
|
+
gem "rake"
|
9
9
|
|
10
|
-
gem "
|
10
|
+
gem "sus", group: [:test]
|
11
|
+
gem "rails", group: [:test]
|
12
|
+
gem "rouge", group: [:docs]
|
13
|
+
gem "listen", group: [:docs]
|
14
|
+
gem "webrick", group: [:docs]
|
15
|
+
gem "zeitwerk", group: [:docs]
|
16
|
+
gem "redcarpet", group: [:docs]
|
17
|
+
gem "combustion", group: [:test]
|
18
|
+
gem "benchmark-ips"
|
19
|
+
gem "htmlbeautifier", group: [:docs]
|
20
|
+
gem "benchmark-memory"
|
21
|
+
gem "rubocop", require: false, github: "joeldrapper/rubocop", branch: "rubocop-user-agent"
|
22
|
+
gem "syntax_suggest"
|
data/README.md
CHANGED
@@ -1,43 +1,27 @@
|
|
1
|
-
|
1
|
+
<a href="https://www.phlex.fun"><img alt="Phlex logo" src="phlex_logo.png" width="180" /></a>
|
2
2
|
|
3
|
-
|
3
|
+
Phlex is a framework that lets you compose web views in pure Ruby.
|
4
4
|
|
5
|
-
|
5
|
+
### Documentation 📗
|
6
6
|
|
7
|
-
|
7
|
+
Documentation can be found at [www.phlex.fun](https://www.phlex.fun).
|
8
8
|
|
9
|
-
|
9
|
+
### Support ✋
|
10
10
|
|
11
|
-
|
12
|
-
gem 'phlex'
|
13
|
-
```
|
11
|
+
If you run into any trouble, please [start a discussion](https://github.com/joeldrapper/phlex/discussions/new), or [open an issue](https://github.com/joeldrapper/phlex/issues/new) if you think you’ve found a bug.
|
14
12
|
|
15
|
-
|
13
|
+
### Community 🙌
|
16
14
|
|
17
|
-
|
15
|
+
Everyone interacting in Phlex codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/joeldrapper/phlex/blob/main/CODE_OF_CONDUCT.md).
|
18
16
|
|
19
|
-
|
17
|
+
### Sponsorship 💖
|
20
18
|
|
21
|
-
|
19
|
+
Maintaining a library like this is a lot of work. Phlex is being actively developed and maintained by **[Joel Drapper](https://github.com/sponsors/joeldrapper)**. If your company benefits from this work or is likely to benefit from it in the future, please consider sponsorship.
|
22
20
|
|
23
|
-
|
21
|
+
### Security 👮
|
24
22
|
|
25
|
-
|
23
|
+
If you’ve found a potential security issue, please email [joel+security@drapper.me](mailto:joel+security@drapper.me).
|
26
24
|
|
27
|
-
|
25
|
+
### Thanks 🙏
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
-
|
33
|
-
## Contributing
|
34
|
-
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/phlex. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/phlex/blob/master/CODE_OF_CONDUCT.md).
|
36
|
-
|
37
|
-
## License
|
38
|
-
|
39
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
40
|
-
|
41
|
-
## Code of Conduct
|
42
|
-
|
43
|
-
Everyone interacting in the Phlex project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/phlex/blob/master/CODE_OF_CONDUCT.md).
|
27
|
+
Thanks [Logology](https://www.logology.co) for sponsoring our logo.
|
data/Rakefile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
require "rake/testtask"
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
5
|
+
begin
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
8
|
end
|
11
9
|
|
12
|
-
task default: :
|
10
|
+
task default: :spec
|
data/bench.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "phlex"
|
5
|
+
require "benchmark/ips"
|
6
|
+
|
7
|
+
require_relative "fixtures/page"
|
8
|
+
require_relative "fixtures/layout"
|
9
|
+
|
10
|
+
puts RUBY_DESCRIPTION
|
11
|
+
|
12
|
+
Benchmark.ips do |x|
|
13
|
+
x.report("Page") { Example::Page.new.call }
|
14
|
+
end
|
data/config.ru
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
@tailwind base;
|
2
|
+
@tailwind components;
|
3
|
+
@tailwind utilities;
|
4
|
+
|
5
|
+
.tabs input[type="radio"]:checked + label {
|
6
|
+
background: #f8f8f8;
|
7
|
+
z-index: 1;
|
8
|
+
margin-bottom: -1px;
|
9
|
+
padding-bottom: 1px;
|
10
|
+
}
|
11
|
+
|
12
|
+
.tabs input[type="radio"]:focus + label {
|
13
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
14
|
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
15
|
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
16
|
+
}
|
17
|
+
|
18
|
+
.tabs input[type="radio"]:checked + label + .tab {
|
19
|
+
display: block;
|
20
|
+
}
|
21
|
+
|
22
|
+
p {
|
23
|
+
@apply my-5
|
24
|
+
}
|
Binary file
|
data/docs/build.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "phlex"
|
5
|
+
require "bundler"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
Bundler.require :docs
|
9
|
+
|
10
|
+
Zeitwerk::Loader.new.tap do |loader|
|
11
|
+
loader.push_dir(__dir__)
|
12
|
+
loader.ignore(__FILE__)
|
13
|
+
loader.setup
|
14
|
+
loader.eager_load
|
15
|
+
end
|
16
|
+
|
17
|
+
FileUtils.mkdir_p("#{__dir__}/dist")
|
18
|
+
FileUtils.cp_r("#{__dir__}/assets", "#{__dir__}/dist")
|
19
|
+
|
20
|
+
PageBuilder.build_all
|
21
|
+
|
22
|
+
system "npx tailwindcss -i ./docs/assets/application.css -o ./docs/dist/application.css"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class CodeBlock < Phlex::Component
|
5
|
+
FORMATTER = Rouge::Formatters::HTML.new
|
6
|
+
|
7
|
+
def initialize(code, syntax:)
|
8
|
+
@code = code
|
9
|
+
@syntax = syntax
|
10
|
+
end
|
11
|
+
|
12
|
+
def template
|
13
|
+
pre(class: "highlight p-5 whitespace-pre-wrap bg-stone-50") {
|
14
|
+
raw FORMATTER.format(
|
15
|
+
lexer.lex(@code)
|
16
|
+
)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def lexer
|
23
|
+
Rouge::Lexer.find(@syntax)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class Example < Phlex::Component
|
5
|
+
def initialize
|
6
|
+
@sandbox = Module.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def template(&block)
|
10
|
+
render Tabs.new do |t|
|
11
|
+
@t = t
|
12
|
+
yield self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def tab(name, code)
|
17
|
+
@t.tab(name) do
|
18
|
+
render CodeBlock.new(code, syntax: :ruby)
|
19
|
+
end
|
20
|
+
|
21
|
+
@sandbox.instance_eval(code)
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute(code)
|
25
|
+
output = @sandbox.instance_eval(code)
|
26
|
+
|
27
|
+
@t.tab("HTML Output") do
|
28
|
+
render CodeBlock.new(HtmlBeautifier.beautify(output), syntax: :html)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class Layout < Phlex::Component
|
5
|
+
register_element :style
|
6
|
+
|
7
|
+
def initialize(title:)
|
8
|
+
@title = title
|
9
|
+
end
|
10
|
+
|
11
|
+
def template(&block)
|
12
|
+
doctype
|
13
|
+
|
14
|
+
html do
|
15
|
+
head do
|
16
|
+
meta charset: "utf-8"
|
17
|
+
title @title
|
18
|
+
link href: "/application.css", rel: "stylesheet"
|
19
|
+
style { raw Rouge::Theme.find("github").render(scope: ".highlight") }
|
20
|
+
end
|
21
|
+
|
22
|
+
body class: "p-12" do
|
23
|
+
div class: "max-w-screen-lg mx-auto grid grid-cols-4 gap-10" do
|
24
|
+
header class: "col-span-1" do
|
25
|
+
a(href: "/", class: "block") { img src: "/assets/logo.png", width: "150" }
|
26
|
+
|
27
|
+
nav do
|
28
|
+
ul do
|
29
|
+
li { a "Introduction", href: "/" }
|
30
|
+
li { a "Templates", href: "/templates" }
|
31
|
+
li { a "Components", href: "/components" }
|
32
|
+
li { a "Source code", href: "https://github.com/joeldrapper/phlex" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
main(class: "col-span-3", &block)
|
38
|
+
|
39
|
+
footer class: "text-sm text-right col-span-4 py-10"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class Markdown < Phlex::Component
|
5
|
+
class Render < Redcarpet::Render::HTML
|
6
|
+
def header(text, level)
|
7
|
+
case level
|
8
|
+
when 1
|
9
|
+
Title.new.call { text }
|
10
|
+
else
|
11
|
+
Heading.new.call { text }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
MARKDOWN = Redcarpet::Markdown.new(Render.new, autolink: true, tables: true)
|
17
|
+
|
18
|
+
def initialize(content)
|
19
|
+
@content = content
|
20
|
+
end
|
21
|
+
|
22
|
+
def template
|
23
|
+
raw MARKDOWN.render(@content)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class Tabs
|
5
|
+
class Tab < Phlex::Component
|
6
|
+
def initialize(name:, checked:)
|
7
|
+
@name = name
|
8
|
+
@checked = checked
|
9
|
+
end
|
10
|
+
|
11
|
+
def template(&block)
|
12
|
+
input class: "opacity-0 fixed peer", type: "radio", name: @_parent.unique_identifier, id: unique_identifier, checked: @checked
|
13
|
+
|
14
|
+
label @name, id: "#{unique_identifier}-label", for: unique_identifier, role: "tab", aria_controls: "#{unique_identifier}-panel", class: "order-1 py-2 px-5 bg-white text-sm border border-b-0 border-l-0 font-medium first-of-type:border-l first-of-type:rounded-tl last-of-type:rounded-tr before:absolute before:pointer-events-none before:w-full before:ring before:h-full before:left-0 before:top-0 before:hidden before:rounded peer-focus:before:block cursor-pointer"
|
15
|
+
|
16
|
+
div id: "#{unique_identifier}-panel", role: "tabpanel", aria_labelledby: "#{unique_identifier}-label", class: "tab hidden order-2 w-full border rounded-b rounded-tr overflow-hidden" do
|
17
|
+
@_parent.instance_exec(&block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def unique_identifier
|
22
|
+
@unique_identifier ||= SecureRandom.hex
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Components
|
4
|
+
class Tabs < Phlex::Component
|
5
|
+
def initialize
|
6
|
+
@index = 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def template(&block)
|
10
|
+
div class: "tabs flex flex-wrap relative my-5", role: "tablist" do
|
11
|
+
content(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def tab(name, &block)
|
16
|
+
render(Tab.new(name: name, checked: first?), &block)
|
17
|
+
@index += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def unique_identifier
|
21
|
+
@unique_identifier ||= SecureRandom.hex
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def first?
|
27
|
+
@index == 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PageBuilder
|
4
|
+
ROOT = Pages::ApplicationPage
|
5
|
+
|
6
|
+
def self.build_all
|
7
|
+
ROOT.subclasses.each { |page| new(page).call }
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(page)
|
11
|
+
@page = page
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
FileUtils.mkdir_p(directory)
|
16
|
+
File.write(file, @page.new.call)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def file
|
22
|
+
"#{directory}/index.html"
|
23
|
+
end
|
24
|
+
|
25
|
+
def directory
|
26
|
+
if path == "index"
|
27
|
+
"#{__dir__}/dist"
|
28
|
+
else
|
29
|
+
"#{__dir__}/dist/#{path}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def path
|
34
|
+
@page.name.downcase.split("::")[1..].join("/")
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pages
|
4
|
+
class Components < ApplicationPage
|
5
|
+
def template
|
6
|
+
render Layout.new(title: "Components in Phlex") do
|
7
|
+
render Markdown.new(<<~MD)
|
8
|
+
# Components
|
9
|
+
|
10
|
+
## Yielding content
|
11
|
+
|
12
|
+
Your components can accept content as a block passed to the template method. You can capture the content block and pass it to the `content` method to yield it.
|
13
|
+
MD
|
14
|
+
|
15
|
+
render Example.new do |e|
|
16
|
+
e.tab "card.rb", <<~RUBY
|
17
|
+
class Card < Phlex::Component
|
18
|
+
def template(&)
|
19
|
+
article(class: "drop-shadow rounded p-5") {
|
20
|
+
h1 "Amazing content!"
|
21
|
+
content(&)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
|
27
|
+
e.execute "Card.new.call { 'Your content here.\n' }"
|
28
|
+
end
|
29
|
+
|
30
|
+
render Markdown.new(<<~MD)
|
31
|
+
## Delegating content
|
32
|
+
|
33
|
+
Alternatively, you can pass the content down as an argument to another component or tag.
|
34
|
+
MD
|
35
|
+
|
36
|
+
render Example.new do |e|
|
37
|
+
e.tab "card.rb", <<~RUBY
|
38
|
+
class Card < Phlex::Component
|
39
|
+
def template(&)
|
40
|
+
article(class: "drop-shadow rounded p-5", &)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
RUBY
|
44
|
+
|
45
|
+
e.execute "Card.new.call { 'Your content here.' }"
|
46
|
+
end
|
47
|
+
|
48
|
+
render Markdown.new(<<~MD)
|
49
|
+
## Nested components
|
50
|
+
|
51
|
+
Components can render other components and optionally pass them content as a block.
|
52
|
+
MD
|
53
|
+
|
54
|
+
render Example.new do |e|
|
55
|
+
e.tab "example.rb", <<~RUBY
|
56
|
+
class Example < Phlex::Component
|
57
|
+
def template
|
58
|
+
render Card.new do
|
59
|
+
h1 "Hello"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
RUBY
|
64
|
+
|
65
|
+
e.tab "card.rb", <<~RUBY
|
66
|
+
class Card < Phlex::Component
|
67
|
+
def template(&)
|
68
|
+
article(class: "drop-shadow rounded p-5", &)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
RUBY
|
72
|
+
|
73
|
+
e.execute "Example.new.call"
|
74
|
+
end
|
75
|
+
|
76
|
+
render Markdown.new(<<~MD)
|
77
|
+
If the block just wraps a string, the string is treated as _text content_.
|
78
|
+
MD
|
79
|
+
|
80
|
+
render Example.new do |e|
|
81
|
+
e.tab "example.rb", <<~RUBY
|
82
|
+
class Example < Phlex::Component
|
83
|
+
def template
|
84
|
+
render(Card.new) { "Hi" }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
RUBY
|
88
|
+
|
89
|
+
e.tab "card.rb", <<~RUBY
|
90
|
+
class Card < Phlex::Component
|
91
|
+
def template(&)
|
92
|
+
article(class: "drop-shadow rounded p-5", &)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
|
97
|
+
e.execute "Example.new.call"
|
98
|
+
end
|
99
|
+
|
100
|
+
render Markdown.new(<<~MD)
|
101
|
+
## Component attributes
|
102
|
+
|
103
|
+
Besides content, components can define attributes in an initializer, which can then be rendered in the template.
|
104
|
+
MD
|
105
|
+
|
106
|
+
render Example.new do |e|
|
107
|
+
e.tab "hello.rb", <<~RUBY
|
108
|
+
class Hello < Phlex::Component
|
109
|
+
def initialize(name:)
|
110
|
+
@name = name
|
111
|
+
end
|
112
|
+
|
113
|
+
def template
|
114
|
+
h1 "Hello \#{@name}!"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
RUBY
|
118
|
+
|
119
|
+
e.tab "example.rb", <<~RUBY
|
120
|
+
class Example < Phlex::Component
|
121
|
+
def template
|
122
|
+
render Hello.new(name: "Joel")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
RUBY
|
126
|
+
|
127
|
+
e.execute "Example.new.call"
|
128
|
+
end
|
129
|
+
|
130
|
+
render Markdown.new(<<~MD)
|
131
|
+
It’s usually a good idea to use instance variables directly rather than creating accessor methods for them. Otherwise it’s easy to run into naming conflicts. For example, your layout component might have the attribute `title`, to render into a `<title>` element in the document head. If you define `attr_accessor :title`, that would overwrite the `title` method for creating `<title>` elements.
|
132
|
+
|
133
|
+
## Calculations with methods
|
134
|
+
|
135
|
+
Components are just Ruby classes, so you can perform calculations on component attributes by defining your own methods.
|
136
|
+
MD
|
137
|
+
|
138
|
+
render Example.new do |e|
|
139
|
+
e.tab "status.rb", <<~RUBY
|
140
|
+
class Status < Phlex::Component
|
141
|
+
def initialize(status:)
|
142
|
+
@status = status
|
143
|
+
end
|
144
|
+
|
145
|
+
def template
|
146
|
+
span status_emoji
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def status_emoji
|
152
|
+
case @status
|
153
|
+
when :success
|
154
|
+
"✅"
|
155
|
+
when :failure
|
156
|
+
"❌"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
RUBY
|
161
|
+
|
162
|
+
e.tab "example.rb", <<~RUBY
|
163
|
+
class Example < Phlex::Component
|
164
|
+
def template
|
165
|
+
render Status.new(status: :success)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
RUBY
|
169
|
+
|
170
|
+
e.execute "Example.new.call"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|