├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── docs ├── creating-a-component.md └── introduction.md ├── examples ├── block_component.rb └── tag_component.rb ├── jekyll-spark.gemspec ├── lib ├── jekyll-spark.rb └── jekyll │ ├── spark.rb │ └── spark │ ├── base.rb │ ├── block.rb │ ├── tag.rb │ └── version.rb └── test ├── helper.rb ├── source ├── _config.yml ├── _plugins │ ├── components │ │ ├── blocks │ │ │ ├── container.rb │ │ │ ├── nested.rb │ │ │ └── wistia_popover.rb │ │ └── tags │ │ │ ├── image.rb │ │ │ └── wistia.rb │ └── jekyll-spark.rb ├── _posts │ └── 2016-01-01-base.md └── components │ ├── container.md │ ├── image-markdown.md │ ├── image.html │ ├── test-nested.md │ ├── wistia-markdown.md │ ├── wistia-popover.md │ └── wistia.html ├── test_component_container.rb ├── test_component_image.rb ├── test_component_props.rb ├── test_component_test_nested.rb ├── test_component_wistia.rb ├── test_component_wistia_popover.rb └── test_unit_spark_base.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.bundle/ 3 | /.yardoc 4 | /Gemfile.lock 5 | /_yardoc/ 6 | /coverage/ 7 | /doc/ 8 | /pkg/ 9 | /spec/reports/ 10 | /tmp/ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.3.3 5 | before_install: gem install bundler -v 1.13.7 6 | 7 | install: 8 | - bundle install 9 | 10 | script: 11 | - bundle exec rake test 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'coveralls', require: false 4 | gem 'simplecov', require: false 5 | 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Help Scout 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spark ✨ [![Build Status](https://travis-ci.org/helpscout/jekyll-spark.svg?branch=master)](https://travis-ci.org/helpscout/jekyll-spark) [![Gem Version](https://badge.fury.io/rb/jekyll-spark.svg)](https://badge.fury.io/rb/jekyll-spark) [![Coverage Status](https://coveralls.io/repos/github/helpscout/jekyll-spark/badge.svg?branch=master)](https://coveralls.io/github/helpscout/jekyll-spark?branch=master) 2 | 3 | A Jekyll library for building fast component-based UI. 4 | 5 | This library was heavily inspired by view/component creation from modern Javascript libraries like [React](https://facebook.github.io/react/) and [Vue](https://vuejs.org/). 6 | 7 | **Table of Contents** 8 | 9 | - [Install](#install) 10 | - [Documentation](#documenation) 11 | - [Examples](#examples) 12 | 13 | ## Install 14 | 15 | Add this line to your application's Gemfile: 16 | 17 | ```ruby 18 | gem 'jekyll-spark' 19 | ``` 20 | 21 | And then execute: 22 | ``` 23 | bundle 24 | ``` 25 | 26 | Or install it yourself as: 27 | ``` 28 | gem install jekyll-spark 29 | ``` 30 | 31 | 32 | 33 | ## Documentation 34 | 35 | **[View the docs](https://github.com/helpscout/jekyll-spark/blob/master/docs/introduction.md)** to get started with Jekyll Components! 36 | 37 | 38 | ## Examples 39 | 40 | **[View the starter](https://github.com/helpscout/jekyll-spark/tree/master/examples)** Component view files. 41 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rubygems' 3 | require 'rake' 4 | require 'rdoc' 5 | require 'date' 6 | require 'yaml' 7 | require 'rake/testtask' 8 | 9 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib])) 10 | require 'jekyll/version' 11 | 12 | Rake::TestTask.new(:test) do |test| 13 | test.libs << 'lib' << 'test' 14 | test.pattern = 'test/**/test_*.rb' 15 | test.verbose = true 16 | end 17 | -------------------------------------------------------------------------------- /docs/creating-a-component.md: -------------------------------------------------------------------------------- 1 | # Creating a component 2 | 3 | Let's create our first component under `_plugins/tags`. 4 | 5 | We'll call it `napolean.rb`! Below is the [starting template](https://github.com/helpscout/jekyll-spark/tree/master/examples) of any Jekyll Component: 6 | 7 | ```ruby 8 | require "jekyll-spark" 9 | 10 | module Jekyll 11 | # Create your component class 12 | class NapoleanComponent < ComponentTag 13 | def template(context) 14 | render = %Q[ 15 | # Put markup here! 16 | ] 17 | end 18 | end 19 | end 20 | 21 | # Register your component with Liquid 22 | Liquid::Template.register_tag( 23 | "Napolean", # Namespace your component 24 | Jekyll::NapoleanComponent, # Pass your newly created component class 25 | ) 26 | ``` 27 | 28 | ### Rendering markup 29 | 30 | Let's make this component render this GIF: 31 | 32 | ![Happy hands](https://media.giphy.com/media/9QbDWTcnq4wmc/giphy.gif) 33 | 34 | 35 | ```ruby 36 | require "jekyll-spark" 37 | 38 | module Jekyll 39 | class NapoleanComponent < ComponentTag 40 | def template(context) 41 | render = %Q[ 42 |
43 | 44 |
45 | ] 46 | end 47 | end 48 | end 49 | 50 | Liquid::Template.register_tag( 51 | "Napolean", 52 | Jekyll::NapoleanComponent, 53 | ) 54 | ``` 55 | 56 | To use our tag component, all we have to do is use `{% Napolean %}` in our Jekyll `.html` or `.md` file: 57 | 58 | ```html 59 | Check out this component! 60 | {% Napolean %} 61 | ``` 62 | 63 | That's it! 64 | 65 | 66 | ### Adding props 67 | 68 | "Props" are data that we can pass to our component. 69 | 70 | For our example, let's provide the ability to adjust the width of our image as well as provide a caption: 71 | 72 | ```html 73 | {% Napolean 74 | width: "300" 75 | caption: "Way to eat all the friggen chips Kip!" 76 | %} 77 | ``` 78 | 79 | You can totally write everything in a single line (below). However, we recommend the above approach as it makes it much easier to add/remove/edit props. 80 | 81 | ```html 82 | {% Napolean width: "300" caption: "Way to eat all the friggen chips Kip!" %} 83 | ``` 84 | 85 | In order to use our new `width` and `caption` props, we have to update our `template` method in our Napolean component. Prop data being passed to our component will be available in a `@prop` instance variable in our `.rb` file: 86 | 87 | ```ruby 88 | require "jekyll-spark" 89 | 90 | module Jekyll 91 | class NapoleanComponent < ComponentTag 92 | def template(context) 93 | # Declare props as variables 94 | # Not necessary, but highly recommended 95 | caption = @props["caption"] 96 | width = @props["width"] 97 | 98 | render = %Q[ 99 |
100 | 101 | #{caption} 102 |
103 | ] 104 | end 105 | end 106 | end 107 | 108 | Liquid::Template.register_tag( 109 | "Napolean", 110 | Jekyll::NapoleanComponent, 111 | ) 112 | ``` 113 | 114 | The resulting compiled markup will be: 115 | 116 | ```html 117 |
118 | 119 | Way to eat all the friggen chips Kip! 120 |
121 | ``` 122 | 123 | 124 | ### Syntax 125 | 126 | Below is a comparison between some syntax differences between Jekyll Components (Ruby) and React (ES6 Javascript): 127 | 128 | | Ruby | Javascript (React) | Description | 129 | | --- | --- | --- | 130 | | `@props["key"]` | `this.props.key` | Component prop data. | 131 | | `render =` | `render() { return ... } ` | Outputting the component's markup. | 132 | | `%Q[ ... ]` | `` `...` `` | String interpolation wrapper. | 133 | | `#{var}` | `${var} ` | String interpolated variable. | 134 | 135 | 136 | 137 | ## Creating a block component 138 | 139 | Let's say we want to update our component to be a block instead. It'll make it more intuitive to add caption. Maybe something like: 140 | 141 | ```html 142 | {% Napolean width: "300" %} 143 | This tastes like the cow got into an onion patch. 144 | {% endNapolean %} 145 | ``` 146 | 147 | It's pretty easy! The only thing we need to change in our `napolean.rb` file is the Class our component inherits from. 148 | 149 | Before: 150 | ```ruby 151 | class NapoleanComponent < ComponentTag 152 | ``` 153 | 154 | After: 155 | ```ruby 156 | class NapoleanComponent < ComponentBlock 157 | ``` 158 | 159 | 160 | ### That's it! 161 | 162 | You've got the basics to create some awesome components in Jekyll. 163 | 164 | ![Yesssssssssssssssss](https://media.giphy.com/media/uTuLngvL9p0Xe/giphy.gif) 165 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Components are `.rb` files that are added to your Jekyll project's default `_plugins/` directory: 4 | 5 | ```shell 6 | my-jekyll/ 7 | └── _plugins/ 8 | └── *Add components here* 9 | ``` 10 | 11 | There are two types are Components: 12 | 13 | **Tags** 14 | 15 | These components are created using Liquid [Tags](http://www.rubydoc.info/github/Shopify/liquid/Liquid/Tag), and they do not contain content when used. 16 | 17 | Example: 18 | ```html 19 | {% Napolean id: "skillz" class: "nunchucks bow staff computer-hacking" %} 20 | ``` 21 | 22 | **Blocks** 23 | 24 | These components are created using Liquid [Blocks](http://www.rubydoc.info/github/Shopify/liquid/Liquid/Block), and they **do** contain content when used. 25 | 26 | Example: 27 | ```html 28 | {% Napolean class: "chapstick" %} 29 | But my lips hurt real bad! 30 | {% endNapolean %} 31 | ``` 32 | 33 | Because of these types, we recommend you organize your components in your `_plugins/` directory into `tags` and `blocks` directories: 34 | 35 | ```shell 36 | my-jekyll/ 37 | └── _plugins/ 38 | ├── blocks/ 39 | └── tags/ 40 | ``` 41 | 42 | 43 | ### Up next 44 | 45 | Learn how to [create a component](creating-a-component.md). 46 | -------------------------------------------------------------------------------- /examples/block_component.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class ExampleBlockComponent < ComponentBlock 5 | def template(context) 6 | # Declare props as variables here 7 | content = @props["content"] 8 | 9 | # Output rendered markup 10 | render = %Q[ 11 |
12 | #{content} 13 |
14 | ] 15 | end 16 | end 17 | end 18 | 19 | Liquid::Template.register_tag( 20 | "ExampleBlock", 21 | Jekyll::ExampleBlockComponent, 22 | ) 23 | -------------------------------------------------------------------------------- /examples/tag_component.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class ExampleTagComponent < ComponentTag 5 | def template(context) 6 | # Declare props as variables here 7 | 8 | # Output rendered markup 9 | render = %Q[ 10 |
11 | Example 12 |
13 | ] 14 | end 15 | end 16 | end 17 | 18 | Liquid::Template.register_tag( 19 | "ExampleTag", 20 | Jekyll::ExampleTagComponent, 21 | ) 22 | -------------------------------------------------------------------------------- /jekyll-spark.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'jekyll/spark/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "jekyll-spark" 8 | spec.version = Jekyll::Spark::VERSION 9 | spec.authors = ["ItsJonQ"] 10 | spec.email = ["itsjonq@gmail.com"] 11 | 12 | spec.summary = "A Jekyll library for building component-based UI" 13 | spec.homepage = "https://github.com/helpscout/jekyll-spark" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 17 | f.match(%r{^(test|spec|features)/}) 18 | end 19 | spec.bindir = "exe" 20 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 21 | spec.require_paths = ["lib"] 22 | 23 | spec.add_runtime_dependency("jekyll", ">= 3.1.2") 24 | spec.add_runtime_dependency("htmlcompressor", "~> 0.3.1") 25 | 26 | spec.add_development_dependency "bundler", "~> 1.13" 27 | spec.add_development_dependency "rake", "~> 10.0" 28 | spec.add_development_dependency "minitest-reporters" 29 | spec.add_development_dependency "minitest-profile" 30 | spec.add_development_dependency "minitest", "~> 5.8" 31 | spec.add_development_dependency "rspec-mocks" 32 | spec.add_development_dependency "jekyll-joule", "~> 0.2.0" 33 | spec.add_development_dependency "shoulda" 34 | spec.add_development_dependency "kramdown" 35 | end 36 | -------------------------------------------------------------------------------- /lib/jekyll-spark.rb: -------------------------------------------------------------------------------- 1 | require "jekyll/spark" 2 | -------------------------------------------------------------------------------- /lib/jekyll/spark.rb: -------------------------------------------------------------------------------- 1 | require "jekyll" 2 | require "jekyll/spark/version" 3 | require "jekyll/spark/block" 4 | require "jekyll/spark/tag" 5 | -------------------------------------------------------------------------------- /lib/jekyll/spark/base.rb: -------------------------------------------------------------------------------- 1 | require "htmlcompressor" 2 | require "jekyll" 3 | require "liquid" 4 | 5 | module Jekyll 6 | module ComponentBase 7 | attr_accessor :props, :content 8 | include Liquid::StandardFilters 9 | 10 | @@compressor = HtmlCompressor::Compressor.new({ 11 | :remove_comments => true, 12 | :remove_intertag_spaces => true, 13 | :preserve_line_breaks => false, 14 | }).freeze 15 | 16 | def initialize(tag_name, markup, tokens) 17 | super 18 | 19 | @attributes = {} 20 | @context = false 21 | @context_name = self.name.to_s.gsub("::", "_").downcase 22 | @content = '' 23 | @default_selector_attr = [] 24 | @props = Hash.new 25 | @site = false 26 | 27 | if markup =~ /(#{Liquid::QuotedFragment}+)?/ 28 | # Parse parameters 29 | # Source: https://gist.github.com/jgatjens/8925165 30 | markup.scan(Liquid::TagAttributes) do |key, value| 31 | @attributes[key] = Liquid::Expression.parse(value) 32 | end 33 | end 34 | end 35 | 36 | # blank? 37 | # Description: Override's Liquid's default blank checker. This allows 38 | # for templates to be used without passing inner content. 39 | def blank? 40 | false 41 | end 42 | 43 | def selector_default_props(attr = @default_selector_attr) 44 | template = "" 45 | attr.each { |prop| 46 | if @props.key?(prop) 47 | template += "#{prop}='#{@props[prop]}' " 48 | end 49 | } 50 | 51 | return template 52 | end 53 | 54 | def selector_data_props(attr = @attributes) 55 | template = "" 56 | attr.keys.each { |key| 57 | if key.include? "data" 58 | template += "#{key.gsub("_", "-")}='#{@props[key]}' " 59 | end 60 | } 61 | 62 | return template 63 | end 64 | 65 | def selector_props(attr = @default_selector_attr) 66 | template = "" 67 | template += selector_data_props 68 | template += selector_default_props(attr) 69 | 70 | return template 71 | end 72 | 73 | def set_props(props = Hash.new) 74 | @props = @props.merge(props) 75 | end 76 | 77 | def serialize_data 78 | data = Hash.new 79 | @attributes["children"] = @content 80 | @attributes["content"] = @content 81 | if @attributes.length 82 | @attributes.each do |key, value| 83 | if @context 84 | value = @context.evaluate(value) 85 | end 86 | data[key] = value 87 | end 88 | end 89 | 90 | return set_props(data) 91 | end 92 | 93 | def unindent(content) 94 | # Remove initial whitespace 95 | content.gsub!(/\A^\s*\n/, "") 96 | # Remove indentations 97 | if content =~ %r!^\s*!m 98 | indentation = Regexp.last_match(0).length 99 | content.gsub!(/^\ {#{indentation}}/, "") 100 | end 101 | 102 | return content 103 | end 104 | 105 | def render(context) 106 | @context = context 107 | @site = @context.registers[:site] 108 | @content = super 109 | serialize_data 110 | output = template(context) 111 | 112 | if (output.instance_of?(String)) 113 | output = Liquid::Template.parse(output).render() 114 | output = @@compressor.compress(unindent(output)) 115 | else 116 | output = "" 117 | end 118 | 119 | return output 120 | end 121 | 122 | def template(context = @context) 123 | return context 124 | end 125 | 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /lib/jekyll/spark/block.rb: -------------------------------------------------------------------------------- 1 | require "jekyll" 2 | require_relative "./base" 3 | 4 | module Jekyll 5 | class ComponentBlock < Liquid::Block 6 | include Jekyll::ComponentBase 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/jekyll/spark/tag.rb: -------------------------------------------------------------------------------- 1 | require "jekyll" 2 | require_relative "./base" 3 | 4 | module Jekyll 5 | class ComponentTag < Liquid::Tag 6 | include Jekyll::ComponentBase 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/jekyll/spark/version.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module Spark 3 | VERSION = "0.6.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require "simplecov" 2 | require "coveralls" 3 | 4 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ 5 | SimpleCov::Formatter::HTMLFormatter, 6 | Coveralls::SimpleCov::Formatter 7 | ] 8 | 9 | SimpleCov.start do 10 | add_filter "/source/" 11 | # add_filter "/test/" 12 | end 13 | 14 | require 'rubygems' 15 | require 'minitest/autorun' 16 | require 'minitest/reporters' 17 | require 'minitest/profile' 18 | require 'nokogiri' 19 | require 'ostruct' 20 | require 'rspec/mocks' 21 | 22 | require 'jekyll' 23 | require 'jekyll/joule' 24 | 25 | Jekyll.logger = Logger.new(StringIO.new) 26 | 27 | require 'kramdown' 28 | require 'shoulda' 29 | 30 | include Jekyll 31 | 32 | # Report with color. 33 | Minitest::Reporters.use! [ 34 | Minitest::Reporters::SpecReporter.new( 35 | :color => true 36 | ) 37 | ] 38 | 39 | class JekyllUnitTest < Minitest::Test 40 | include ::RSpec::Mocks::ExampleMethods 41 | 42 | def setup 43 | @site = Site.new(site_configuration) 44 | @site.read 45 | @site.generate 46 | @site.render 47 | @pages = @site.pages 48 | @joule = Jekyll::Joule::Site.new(@site) 49 | end 50 | 51 | def get_component_page(title) 52 | page = @pages.find { |p| 53 | p.data["title"].downcase === title.downcase and p.data["type"].downcase === "component" 54 | } 55 | 56 | return page ? page["content"] : false 57 | end 58 | 59 | def mocks_expect(*args) 60 | RSpec::Mocks::ExampleMethods::ExpectHost.instance_method(:expect).\ 61 | bind(self).call(*args) 62 | end 63 | 64 | def before_setup 65 | ::RSpec::Mocks.setup 66 | super 67 | end 68 | 69 | def after_teardown 70 | super 71 | ::RSpec::Mocks.verify 72 | ensure 73 | ::RSpec::Mocks.teardown 74 | end 75 | 76 | def fixture_site(overrides = {}) 77 | Jekyll::Site.new(site_configuration(overrides)) 78 | end 79 | 80 | def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS) 81 | Utils.deep_merge_hashes(base_hash, overrides) 82 | .fix_common_issues.backwards_compatibilize.add_default_collections 83 | end 84 | 85 | def site_configuration(overrides = {}) 86 | full_overrides = build_configs(overrides, build_configs({ 87 | "destination" => dest_dir, 88 | "incremental" => false 89 | })) 90 | build_configs({ 91 | "source" => source_dir 92 | }, full_overrides) 93 | end 94 | 95 | def dest_dir(*subdirs) 96 | root_dir('dest', *subdirs) 97 | end 98 | 99 | def source_dir(*subdirs) 100 | root_dir('source', *subdirs) 101 | end 102 | 103 | def clear_dest 104 | FileUtils.rm_rf(dest_dir) 105 | FileUtils.rm_rf(source_dir('.jekyll-metadata')) 106 | end 107 | 108 | def root_dir(*subdirs) 109 | File.join(File.dirname(__FILE__), *subdirs) 110 | end 111 | 112 | def directory_with_contents(path) 113 | FileUtils.rm_rf(path) 114 | FileUtils.mkdir(path) 115 | File.open("#{path}/index.html", "w"){ |f| f.write("I was previously generated.") } 116 | end 117 | 118 | def with_env(key, value) 119 | old_value = ENV[key] 120 | ENV[key] = value 121 | yield 122 | ENV[key] = old_value 123 | end 124 | 125 | def capture_output 126 | stderr = StringIO.new 127 | Jekyll.logger = Logger.new stderr 128 | yield 129 | stderr.rewind 130 | return stderr.string.to_s 131 | end 132 | alias_method :capture_stdout, :capture_output 133 | alias_method :capture_stderr, :capture_output 134 | end 135 | -------------------------------------------------------------------------------- /test/source/_config.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | - 3 | scope: 4 | path: "" 5 | values: 6 | layout: null 7 | -------------------------------------------------------------------------------- /test/source/_plugins/components/blocks/container.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class ContainerComponent < ComponentBlock 5 | def template(context) 6 | content = @props["content"] 7 | class_name = @props["class"] 8 | 9 | render = %Q[ 10 |
11 |
12 |
13 | #{content} 14 |
15 |
16 |
17 | ] 18 | end 19 | end 20 | end 21 | 22 | Liquid::Template.register_tag( 23 | "Container", 24 | Jekyll::ContainerComponent, 25 | ) 26 | -------------------------------------------------------------------------------- /test/source/_plugins/components/blocks/nested.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class TestNestedComponent < ComponentBlock 5 | def template(context) 6 | content = @props["content"] 7 | 8 | render = %Q[ 9 |
10 | {% img 11 | src: "test.jpg" 12 | %} 13 | #{content} 14 |
15 | ] 16 | end 17 | end 18 | end 19 | 20 | Liquid::Template.register_tag( 21 | "TestNested", 22 | Jekyll::TestNestedComponent, 23 | ) 24 | -------------------------------------------------------------------------------- /test/source/_plugins/components/blocks/wistia_popover.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class WistiaPopoverComponent < ComponentBlock 5 | def template(context) 6 | unless @props["id"] 7 | return context 8 | end 9 | 10 | id = @props["id"].gsub("wistia_", "").gsub("Wistia_", "") 11 | class_name = @props["class"] 12 | content = @props["content"] 13 | style = @props["style"] || "display:inline-block;height:80px;width:150px" 14 | 15 | render = %Q[ 16 | 17 | 18 | 26 | #{content} 27 | 28 | 29 | ] 30 | end 31 | end 32 | end 33 | 34 | Liquid::Template.register_tag('WistiaPopover', Jekyll::WistiaPopoverComponent) 35 | -------------------------------------------------------------------------------- /test/source/_plugins/components/tags/image.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-spark" 2 | 3 | module Jekyll 4 | class ImageComponent < ComponentTag 5 | DEFAULT_TAG_PROPS = [ 6 | "alt", 7 | "crossorigin", 8 | "id", 9 | "ismap", 10 | "longdesc", 11 | "style", 12 | "title", 13 | "usemap", 14 | ] 15 | BLANK_IMG = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 16 | 17 | def template(context) 18 | prop = @props 19 | 20 | blazy_class_name = "b-lazy" 21 | class_name = prop["class"] 22 | height = prop["height"] 23 | data_src = BLANK_IMG 24 | src = prop["src"] 25 | srcset = prop["srcset"] 26 | title = prop["title"] 27 | width = prop["width"] 28 | responsive = "" 29 | default_props = selector_props(DEFAULT_TAG_PROPS) 30 | 31 | # Responsive 32 | # Adds new responsive-based image attributes: srcset and sizes. 33 | # The numbers are based on @attribute["width"]. 34 | # At the moment, this plugin only supports 1x and 2x pixel-density sizes. 35 | # 36 | # Learn more about srcset 37 | # https://ericportis.com/posts/2014/srcset-sizes/ 38 | # 39 | if srcset 40 | srcset_2x = srcset.to_s.gsub(/\.(?=[^.]*$)/, "@2x.") 41 | srcset_1x = srcset.to_s.gsub(/\.(?=[^.]*$)/, "@1x.") 42 | # Width-based srcset (Recommended) 43 | if (width) 44 | # Ensure that width is just a number (just in case) 45 | w = width.to_s.gsub("px", "").gsub("%", "").to_i 46 | responsive = " 47 | srcset=' 48 | #{srcset_1x} #{w}w, 49 | #{srcset_2x} #{w*2}w 50 | ' 51 | " 52 | responsive += " 53 | sizes=' 54 | (min-width: 40em) #{w}px, 55 | 100vw 56 | ' 57 | " 58 | # Fallback to pixel-density based (if width is not available) 59 | else 60 | responsive = " 61 | srcset=' 62 | #{srcset_1x} 1x, 63 | #{srcset_2x} 2x 64 | ' 65 | " 66 | end 67 | src = srcset_1x 68 | end 69 | 70 | # Set the src for