jekyll-imgflow 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f902d95441ebd7a1c3a98200b140c2bf109343a79cf43700221a6cfec2902df
4
- data.tar.gz: c89e222e5f37bb795f44ec7b6e3ecc865ca3fb66fefac0481ea31211f8fa7dfb
3
+ metadata.gz: fa56ff31b7cfe312dfe202726e049c99db5511dc35b2e82d721abde41c76488e
4
+ data.tar.gz: 88ff3c89e44a1dfe0fe022a016bbeba79185c137bca201c4e2c66c9d06cc5a92
5
5
  SHA512:
6
- metadata.gz: cfc171eb11a3f533033671d2b8d8bfdc90bd3a9790bc9b764cf1ada4e58817179261419c80af2cd0eb2bc8c6bc1c130d754f31a0415664bd4262c43f0c3adb49
7
- data.tar.gz: 1f92eb5e21c373a01e570bcaa843a51ccfd093655c46a64a4ae21248c9d4862d8cd6acac84830e2f1d023a8de4a8b8b2662bfbfe44e83c0a6a633c6eb23cc6d1
6
+ metadata.gz: c1f92e12c629652780356a972969ee435992b29dd62dea03ea263582084e8b512b35eb25e8bb1845c111834a5506cd1c3ffd5c9b4c5392ff6e17e20d3ea5c4ae
7
+ data.tar.gz: 92b73297116fd7dd7d38a5aeb349c81aeab97709f3a85886469dc0443e3fa9e5e35c2c971b1c01bd6682db2c59776c54112682a31e3610c64a6254b3d4067441
@@ -83,7 +83,8 @@ module JekyllImgFlow
83
83
  parent: attributes[:parent] || {},
84
84
  alt: attributes[:alt],
85
85
  link: attributes[:link],
86
- modal: attributes[:modal]
86
+ modal: attributes[:modal],
87
+ modal_results: attributes[:modal_results] || []
87
88
  }
88
89
  end
89
90
 
@@ -96,7 +97,8 @@ module JekyllImgFlow
96
97
  parent: {},
97
98
  alt: nil,
98
99
  link: nil,
99
- modal: nil
100
+ modal: nil,
101
+ modal_results: []
100
102
  }
101
103
  end
102
104
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
+ require "fastimage"
4
5
 
5
6
  module Jekyll
6
7
  class ImgflowTag < Liquid::Tag
@@ -99,11 +100,51 @@ module Jekyll
99
100
  page_path)
100
101
  end
101
102
 
103
+ modal_results = if operations.empty?
104
+ []
105
+ else
106
+ process_modal_variants(components, operations.first, parsed, original_name,
107
+ input_path, page_path)
108
+ end
102
109
  relative_results = results.uniq.map { |result| relative_result_path(result, site) }
110
+ relative_modal_results = modal_results.uniq.map do |result|
111
+ relative_result_path(result, site)
112
+ end
103
113
  parsed = parsed.merge(markup_format: "picture") if relative_results.length > 1
114
+ parsed = parsed.merge(modal_results: relative_modal_results)
104
115
  generate_html(relative_results, parsed, context)
105
116
  end
106
117
 
118
+ def process_modal_variants(components, operation, parsed, original_name, input_path, page_path)
119
+ config = components[:config]
120
+ return [] unless modal_requested?(parsed, config)
121
+
122
+ original_width = FastImage.size(input_path)&.first
123
+ return [] unless original_width
124
+
125
+ params = operation[:params].dup
126
+ formats = Array(params.delete(:formats) || params[:format])
127
+ formats = config.formats if formats.empty?
128
+ modal_width = [original_width, config.sizes.values.max].min
129
+
130
+ formats.map do |format|
131
+ modal_params = params.merge(width: modal_width, format: format)
132
+ process_variant(components, operation, modal_params, original_name, input_path, page_path)
133
+ end
134
+ rescue FastImage::ImageFetchError, FastImage::UnknownImageType
135
+ []
136
+ end
137
+
138
+ def modal_requested?(parsed, config)
139
+ return false if %w[direct_url naked_srcset].include?(parsed[:markup_format])
140
+
141
+ attrs = parsed[:html_attributes] || {}
142
+ return false if attrs[:link]
143
+
144
+ modal = attrs[:modal]
145
+ modal.nil? ? (config.respond_to?(:image_modal) && config.image_modal) : modal.to_s == "true"
146
+ end
147
+
107
148
  def process_variants(components, operation, original_name, input_path, page_path)
108
149
  params = operation[:params].dup
109
150
  formats = Array(params.delete(:formats) || params[:format])
@@ -195,7 +236,8 @@ module Jekyll
195
236
  parent: extract_prefixed_attrs(base_attrs, "parent-"),
196
237
  alt: base_attrs[:alt],
197
238
  link: base_attrs[:link],
198
- modal: base_attrs[:modal]
239
+ modal: base_attrs[:modal],
240
+ modal_results: parsed[:modal_results] || []
199
241
  }
200
242
  end
201
243
 
@@ -20,15 +20,24 @@ module JekyllImgFlow
20
20
 
21
21
  JS = <<~JS
22
22
  (function(){
23
- function openModal(src,alt){
23
+ function openModal(trigger,alt){
24
24
  var o=document.createElement('div');
25
25
  o.className='imgflow-modal-overlay';
26
+ var p=document.createElement('picture');
27
+ var href=trigger.getAttribute('href');
28
+ var types={avif:'image/avif',webp:'image/webp',png:'image/png',jpg:'image/jpeg',jpeg:'image/jpeg'};
29
+ var formats=(trigger.getAttribute('data-imgflow-modal-formats')||'').split(',');
30
+ formats.forEach(function(format){
31
+ if(!format)return;
32
+ var s=document.createElement('source');
33
+ s.srcset=href.replace(/.[^.]+$/,'.'+format);s.type=types[format];p.appendChild(s);});
26
34
  var i=document.createElement('img');
27
- i.src=src;i.alt=alt||'';i.className='imgflow-modal-image';
35
+ i.src=href;
36
+ i.alt=alt||'';i.className='imgflow-modal-image';p.appendChild(i);
28
37
  var b=document.createElement('button');
29
38
  b.className='imgflow-modal-close';b.setAttribute('aria-label','Close');
30
39
  b.innerHTML='&times;';
31
- o.appendChild(b);o.appendChild(i);document.body.appendChild(o);
40
+ o.appendChild(b);o.appendChild(p);document.body.appendChild(o);
32
41
  function c(){document.body.removeChild(o);
33
42
  document.removeEventListener('keydown',k);}
34
43
  function k(e){if(e.key==='Escape')c();}
@@ -38,7 +47,7 @@ module JekyllImgFlow
38
47
  document.addEventListener('click',function(e){
39
48
  var t=e.target.closest('[data-imgflow-modal]');
40
49
  if(!t)return;e.preventDefault();
41
- openModal(t.getAttribute('href'),t.getAttribute('data-imgflow-alt'));});
50
+ openModal(t,t.getAttribute('data-imgflow-alt'));});
42
51
  })();
43
52
  JS
44
53
 
@@ -84,20 +93,72 @@ module JekyllImgFlow
84
93
  @config&.image_modal ? true : false
85
94
  end
86
95
 
87
- # Pick the best image path for the modal — prefer the fallback format
88
- # (e.g. jpg) for maximum browser compatibility
96
+ # Pick the largest existing fallback-format variant for the modal.
89
97
  def modal_image_path
90
- fallback_ext = @config&.fallback_format ? ".#{@config.fallback_format}" : ".jpg"
91
- fallback = @results.find { |r| File.extname(r).downcase == fallback_ext }
92
- fallback || @results.last
98
+ variants = modal_variants
99
+ variants[modal_fallback_format] || @results.last
100
+ end
101
+
102
+ def modal_variants
103
+ candidates = @attributes[:modal_results] + @results + generated_modal_results
104
+ max_width = modal_max_width
105
+
106
+ candidates.each_with_object({}) do |result, variants|
107
+ parsed = JekyllImgFlow::FilenameGenerator.new.parse_filename(File.basename(result))
108
+ next if parsed.empty? || (max_width && parsed[:width] > max_width)
109
+ next if variants[parsed[:format]] && modal_width(variants[parsed[:format]]) >= parsed[:width]
110
+
111
+ variants[parsed[:format]] = result
112
+ end
113
+ end
114
+
115
+ def modal_fallback_format
116
+ @config&.fallback_format.to_s
117
+ end
118
+
119
+ def modal_max_width
120
+ return unless @config.respond_to?(:sizes)
121
+
122
+ @config.sizes.values.max
123
+ end
124
+
125
+ def generated_modal_results
126
+ return [] unless @config && @context&.registers&.key?(:site)
127
+
128
+ result = @results.first
129
+ parsed = JekyllImgFlow::FilenameGenerator.new.parse_filename(File.basename(result))
130
+ return [] if parsed.empty?
131
+
132
+ site = @context.registers[:site]
133
+ directory = File.join(site.source, File.dirname(result.delete_prefix("/")))
134
+ return [] unless File.directory?(directory)
135
+
136
+ max_width = @config.sizes.values.max
137
+ Dir.children(directory).filter_map do |filename|
138
+ candidate = JekyllImgFlow::FilenameGenerator.new.parse_filename(filename)
139
+ next unless candidate[:base_name] == parsed[:base_name]
140
+ next unless candidate[:hash] == parsed[:hash]
141
+ next if max_width && candidate[:width] > max_width
142
+
143
+ File.join(File.dirname(result), filename)
144
+ end
145
+ rescue Errno::ENOENT
146
+ []
147
+ end
148
+
149
+ def modal_width(result)
150
+ JekyllImgFlow::FilenameGenerator.new.parse_filename(File.basename(result))[:width] || 0
93
151
  end
94
152
 
95
153
  # Wrap HTML in modal trigger anchor
96
154
  def wrap_with_modal(html)
97
- modal_href = html_path(modal_image_path)
155
+ variants = modal_variants
156
+ modal_href = html_path(variants[modal_fallback_format] || @results.last)
98
157
  return html unless modal_href
99
158
 
159
+ source_formats = variants.keys.reject { |format| format == modal_fallback_format }
100
160
  data_attrs = " data-imgflow-modal"
161
+ data_attrs += " data-imgflow-modal-formats=\"#{source_formats.join(',')}\"" unless source_formats.empty?
101
162
  alt = @attributes[:alt]
102
163
  data_attrs += " data-imgflow-alt=\"#{escape_attr_value(alt)}\"" if alt
103
164
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllImgFlow
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-imgflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svend Gundestrup