coveragebook_components 0.19.13 → 0.20.0

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: 52831c88c2451c454bdc92e3236b09a653825ad3f9764b988df3b9f5cf207cf1
4
- data.tar.gz: af8169c8f760d5f48611610ca099c8ef68fdfed5248274852a4a1bf3ad0c1970
3
+ metadata.gz: 7174e55d00ea7174b4a8f120730c5314ff329fb91a44dfd3677c7f8232bd3972
4
+ data.tar.gz: ac82cd35db155780993509201e0d3d4ae0b329efa708634af9305f25923c2687
5
5
  SHA512:
6
- metadata.gz: fae19112cfb2acac8a4947359f7cea0f578be89c03c05c269e0f783cc8f54b12ef8fc4e69a327b62014e31aee238925b7f0f8e81f3bb4da411799934ba369903
7
- data.tar.gz: 23b11d2d6300225ce07640627f6432d841146ffe6c84b51f4f6eae30c5ea43be0d7b82d97f9d64a0fc2f0b838932d84d81649cb1eaf8401b36d8747a1c1b28ff
6
+ metadata.gz: 5dea756622a25f146fca33444887a500cd03cd8110b4f34c501c6cbbd142fa3d77b38ee804a1dd960c13b5260d9eea44e2476b08257464b44d059007fa71d78d
7
+ data.tar.gz: 6313414a73a6de9e4f88c09fb6eea5039cafd01978e0633009b8516ff62baef5dfb287c437b0a02e415e677059457032046ae23af1482f1ffaba27fae82870f2
@@ -2942,6 +2942,16 @@ select{
2942
2942
 
2943
2943
  /* Styles */
2944
2944
 
2945
+ [data-coco][data-component="icon"][data-icon] svg {
2946
+ aspect-ratio: 1 / 1;
2947
+ stroke-linecap: round;
2948
+ stroke-linejoin: round;
2949
+ fill: none;
2950
+ stroke: currentColor;
2951
+ stroke-width: 2;
2952
+ color: currentColor;
2953
+ }
2954
+
2945
2955
  [data-coco][data-component="icon"][data-icon]:not([data-style="fill"]):not([data-style="custom"]) svg{
2946
2956
  stroke-linecap: round;
2947
2957
  stroke-linejoin: round;
@@ -5083,6 +5093,16 @@ select{
5083
5093
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
5084
5094
  }
5085
5095
 
5096
+ [data-coco][data-component="modal-lightbox"].\!loaded .modal-lightbox-content{
5097
+ --tw-bg-opacity: 1;
5098
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1))
5099
+ }
5100
+
5101
+ [data-coco][data-component="modal-lightbox"].loaded .modal-lightbox-content{
5102
+ --tw-bg-opacity: 1;
5103
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1))
5104
+ }
5105
+
5086
5106
  [data-coco].coco-link{
5087
5107
  display: inline-flex;
5088
5108
  align-items: center
@@ -6105,10 +6125,6 @@ select{
6105
6125
  display: block
6106
6126
  }
6107
6127
 
6108
- .grid{
6109
- display: grid
6110
- }
6111
-
6112
6128
  .contents{
6113
6129
  display: contents
6114
6130
  }
@@ -13259,6 +13259,7 @@ var require_iro = __commonJS({
13259
13259
  var layout = state.layout;
13260
13260
  if (!Array.isArray(layout)) {
13261
13261
  switch (layout) {
13262
+ // TODO: implement some?
13262
13263
  default:
13263
13264
  layout = [
13264
13265
  { component: IroWheel },
@@ -15343,7 +15344,7 @@ var alpine_default = import_alpinejs.default;
15343
15344
  // ../../../package.json
15344
15345
  var package_default = {
15345
15346
  name: "coveragebook-components",
15346
- version: "0.19.13",
15347
+ version: "0.20.0",
15347
15348
  repository: "git@github.com:coveragebook/coco.git",
15348
15349
  license: "NO LICENSE",
15349
15350
  author: "Mark Perkins <mark@coveragebook.com>",
@@ -15392,12 +15393,12 @@ var package_default = {
15392
15393
  "@percy/cli": "^1.30.0",
15393
15394
  "@tailwindcss/container-queries": "^0.1.0",
15394
15395
  "@tailwindcss/forms": "^0.5.9",
15395
- "alias-hq": "^6.2.2",
15396
+ "alias-hq": "^6.2.4",
15396
15397
  alpinejs: "^3.14.1",
15397
15398
  autoprefixer: "^10.4.16",
15398
15399
  "container-query-polyfill": "^1.0.2",
15399
15400
  del: "^7.1.0",
15400
- esbuild: "^0.23.0",
15401
+ esbuild: "^0.25.3",
15401
15402
  "esbuild-plugin-copy": "^2.0.2",
15402
15403
  "fast-glob": "^3.3.1",
15403
15404
  "fast-sort": "^3.4.1",
@@ -15436,7 +15437,7 @@ var package_default = {
15436
15437
  "npm run build"
15437
15438
  ],
15438
15439
  "after:version:bump": "bundle exec rake coco:gem:bump_version[${version}] && bundle && cd lookbook && bundle",
15439
- "after:release": 'rake "coco:gem:release[${version}]"'
15440
+ "after:release": 'bundle exec rake "coco:gem:release[${version}]"'
15440
15441
  }
15441
15442
  },
15442
15443
  devDependencies: {
@@ -8,6 +8,12 @@
8
8
 
9
9
  /* Styles */
10
10
 
11
+ &[data-icon] svg {
12
+ aspect-ratio: 1 / 1;
13
+
14
+ @apply icon-stroke;
15
+ }
16
+
11
17
  &[data-icon]:not([data-style="fill"]):not([data-style="custom"]) svg {
12
18
  @apply icon-stroke;
13
19
  }
@@ -2,82 +2,43 @@ module Coco
2
2
  class Icon < Coco::Component
3
3
  include Concerns::AcceptsOptions
4
4
 
5
- ICON_CACHE = {}
6
-
7
- ALIASES = {
8
- edit: "pen-line",
9
- "edit-3": "pen-line",
10
- "git-commit": "git-commit-horizontal",
11
- grid: "grid-3x3",
12
- "user-circle": "circle-user",
13
- layout: "panels-top-left",
14
- "alert-circle": "circle-alert",
15
- "check-circle": "circle-check",
16
- "loader-2": "loader-circle",
17
- "alert-triangle": "triangle-alert",
18
- "arrow-right-circle": "circle-arrow-right",
19
- "check-square": "square-check",
20
- "help-circle": "circle-help",
21
- "plus-circle": "circle-plus",
22
- "plus-square": "square-plus",
23
- "bar-chart-2": "chart-no-axes-column",
24
- "bar-chart": "chart-no-axes-column-increasing",
25
- "more-horizontal": "ellipsis",
26
- "more-vertical": "ellipsis-vertical"
27
- }.freeze
28
-
29
- InvalidIconError = Class.new(StandardError)
30
-
31
5
  accepts_option :size, from: %i[xs sm md lg xl xxl full]
32
- accepts_option :spin, from: [true, false]
33
- accepts_option :style, from: [:line, :fill, :custom]
6
+ accepts_option :spin, from: [true, false, nil], default: nil
7
+ accepts_option :style, from: [:line, :fill, :custom, nil], default: nil
34
8
 
35
9
  before_render do
36
- if name
37
- set_tag_data_attr(:icon, name)
38
- set_option_value(:style, :custom) if icon_data[:custom]
39
- end
10
+ set_tag_data_attr(:icon, name) if name.present?
11
+ set_option_value :style, :custom if custom?
40
12
  end
41
13
 
42
14
  def initialize(name: nil, **kwargs)
43
15
  @icon_name = name&.to_s&.tr("_", "-")
44
16
  end
45
17
 
46
- def name
47
- ALIASES.fetch(@icon_name.to_sym, @icon_name) if @icon_name
48
- end
49
-
50
18
  def svg
51
- content || icon_data[:svg]
19
+ if custom?
20
+ IconSet.icon_svg(name)
21
+ elsif name? && content.blank?
22
+ %(<svg><use href="##{symbol_id}"></use></svg>).html_safe
23
+ elsif content.present?
24
+ content.to_s
25
+ elsif Rails.env.development?
26
+ raise "Missing icon '#{@icon_name}'"
27
+ end
52
28
  end
53
29
 
54
- def icon_data
55
- ICON_CACHE[name] ||= {
56
- custom: custom?,
57
- svg: read_svg
58
- }
30
+ def name
31
+ IconSet.resolve_icon_name(@icon_name) if @icon_name.present?
59
32
  end
60
33
 
61
- def custom?
62
- @_custom ||= File.exist?(custom_icon_path)
63
- end
34
+ def name? = name.present?
64
35
 
65
- def read_svg
66
- if custom?
67
- File.read(custom_icon_path).html_safe
68
- elsif File.exist?(icon_path)
69
- File.read(icon_path).html_safe
70
- elsif Rails.env.development? || Rails.env.test?
71
- raise InvalidIconError, "`#{name}` is not a valid icon name"
72
- end
73
- end
36
+ def custom? = IconSet.custom_icon?(name)
74
37
 
75
- def icon_path
76
- @_icon_path ||= Coco::Engine.root.join("app/assets/build/coco/icons/#{name}.svg")
77
- end
38
+ protected
78
39
 
79
- def custom_icon_path
80
- @_custom_icon_path ||= Coco::Engine.root.join("app/assets/build/coco/icons/custom/#{name}.svg")
40
+ def symbol_id
41
+ IconSet.symbol_id_for_icon(name)
81
42
  end
82
43
  end
83
44
  end
@@ -29,6 +29,7 @@
29
29
  &.loaded {
30
30
  .modal-lightbox-content {
31
31
  @apply shadow-2xl;
32
+ @apply bg-background-light-1;
32
33
  }
33
34
  }
34
35
  }
@@ -102,8 +102,14 @@ module Coco
102
102
  render Coco::Avatar.new(src: src, name: name, **)
103
103
  end
104
104
 
105
- def coco_icon(icon_name = nil, **, &)
106
- render(Coco::Icon.new(name: icon_name, **), &)
105
+ def coco_icon(icon_name = nil, **kwargs, &)
106
+ kwargs[:name] = icon_name if icon_name.present?
107
+ render(Coco::Icon.new(**kwargs), &)
108
+ end
109
+
110
+ def coco_icon_sprite(*extra_icons)
111
+ icons = [*Coco::IconSet.icons, *extra_icons]
112
+ Coco::IconSprite.new(icons).to_svg
107
113
  end
108
114
 
109
115
  # Indicators
data/config/coco.yml ADDED
@@ -0,0 +1,12 @@
1
+ shared:
2
+ icons:
3
+ include: []
4
+ aliases: {}
5
+ custom:
6
+ - eye-off-red
7
+ - google-sso
8
+ - layout-overlay
9
+ - layout-split
10
+ - layout-stacked
11
+ - microsoft-sso
12
+ - montage
data/lib/coco/engine.rb CHANGED
@@ -14,6 +14,18 @@ module Coco
14
14
  #{root}/app/helpers
15
15
  ]
16
16
 
17
+ initializer "coco.config" do |app|
18
+ default_config = app.config_for(Engine.root.join("config/coco.yml")) || {}
19
+ host_config = begin
20
+ app.config_for(:coco) || {}
21
+ rescue
22
+ {}
23
+ end
24
+
25
+ merged_config = default_config.deep_merge(host_config)
26
+ app.config.coco = ActiveSupport::OrderedOptions.new.merge!(merged_config)
27
+ end
28
+
17
29
  initializer "coco.autoloading" do |app|
18
30
  coco_dir = "#{root}/app/components/coco"
19
31
 
@@ -0,0 +1,57 @@
1
+ module Coco
2
+ module IconSet
3
+ ICON_DIR = "app/assets/build/coco/icons"
4
+ ICON_NAMES_FILE = "config/icons.json"
5
+
6
+ InvalidIconError = Class.new(StandardError)
7
+
8
+ class << self
9
+ def icons
10
+ if Coco.env.development?
11
+ [*all_icons, *Coco.config.icons[:include]].uniq
12
+ else
13
+ Coco.config.icons[:include]&.uniq || []
14
+ end
15
+ end
16
+
17
+ def aliases
18
+ Coco.config.icons[:aliases]&.stringify_keys || {}
19
+ end
20
+
21
+ def all_icons
22
+ @all_icons ||= JSON.load_file(Engine.root.join(ICON_NAMES_FILE))
23
+ end
24
+
25
+ def resolve_icon_name(name)
26
+ aliases.fetch(name.to_s, name)
27
+ end
28
+
29
+ def resolve_icon_path(icon_name)
30
+ search_paths = [
31
+ Engine.root.join(ICON_DIR, "custom", "#{icon_name}.svg"),
32
+ Engine.root.join(ICON_DIR, "#{icon_name}.svg")
33
+ ]
34
+
35
+ search_paths.find { _1.exist? }
36
+ end
37
+
38
+ def symbol_id_for_icon(icon_name)
39
+ "svg_icon_#{icon_name.tr("-", "_")}" if icon_name.present?
40
+ end
41
+
42
+ def custom_icon?(icon_name)
43
+ icon_name && Coco.config.icons[:custom].include?(icon_name)
44
+ end
45
+
46
+ def icon_svg(icon_name)
47
+ icon_path = resolve_icon_path(icon_name)
48
+
49
+ if icon_path.present? && icon_path&.exist?
50
+ File.read(icon_path).html_safe
51
+ end
52
+ rescue
53
+ # Do nothing...
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ module Coco
2
+ class IconSprite
3
+ include ActionView::Helpers::TagHelper
4
+
5
+ def initialize(icons = [])
6
+ @icons = icons
7
+ @icons.freeze
8
+ end
9
+
10
+ def to_svg
11
+ tag.svg xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" do
12
+ ("\n" + symbols.join("\n ") + "\n").html_safe
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ def symbols
19
+ @icons.map { symbol_for_icon(_1) }.compact
20
+ end
21
+
22
+ def symbol_for_icon(icon_name)
23
+ return unless icon_name.present?
24
+
25
+ svg_content = IconSet.icon_svg(icon_name)
26
+
27
+ # Extract the path data and attributes from the SVG
28
+ doc = Nokogiri::XML(svg_content)
29
+ svg_element = doc.at_css("svg")
30
+
31
+ # Extract SVG attributes that should be preserved on the symbol
32
+ preserved_attrs = %w[fill stroke stroke-width stroke-linecap stroke-linejoin viewBox style]
33
+ .index_with { svg_element[_1] }
34
+ .symbolize_keys
35
+ .with_defaults(viewBox: "0 0 24 24")
36
+ .compact
37
+
38
+ tag.symbol id: IconSet.symbol_id_for_icon(icon_name), **preserved_attrs do
39
+ svg_element.children.map(&:to_s).join.html_safe # rubocop:disable Rails/OutputSafety
40
+ end
41
+ rescue
42
+ nil
43
+ end
44
+ end
45
+ end
data/lib/coco.rb CHANGED
@@ -1,3 +1,11 @@
1
1
  module Coco
2
- VERSION = "0.19.13"
2
+ VERSION = "0.20.0"
3
+
4
+ class << self
5
+ def config = Rails.application.config.coco
6
+
7
+ def env
8
+ @env ||= ActiveSupport::StringInquirer.new(ENV["COCO_ENV"] || "production")
9
+ end
10
+ end
3
11
  end
@@ -19,6 +19,10 @@
19
19
  max-width: <%= lookbook_display(:max_width, "800px") %>;
20
20
  ">
21
21
  <%= yield %>
22
+
23
+ <div hidden>
24
+ <%= coco_icon_sprite %>
25
+ </div>
22
26
  </div>
23
27
  </body>
24
28
  </html>
@@ -8,5 +8,9 @@
8
8
  </head>
9
9
  <body>
10
10
  <%= yield %>
11
+
12
+ <div hidden>
13
+ <%= coco_icon_sprite %>
14
+ </div>
11
15
  </body>
12
- </html>
16
+ </html>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coveragebook_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.13
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Perkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-29 00:00:00.000000000 Z
11
+ date: 2025-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -1896,6 +1896,7 @@ files:
1896
1896
  - app/helpers/coco/integration_helper.rb
1897
1897
  - app/helpers/coco/modal_helper.rb
1898
1898
  - app/helpers/coco/tag_helper.rb
1899
+ - config/coco.yml
1899
1900
  - config/esbuild.config.mjs
1900
1901
  - config/exports.js
1901
1902
  - config/icons.json
@@ -1909,6 +1910,8 @@ files:
1909
1910
  - lib/coco/app_form_builder.rb
1910
1911
  - lib/coco/component_resolver.rb
1911
1912
  - lib/coco/engine.rb
1913
+ - lib/coco/icon_set.rb
1914
+ - lib/coco/icon_sprite.rb
1912
1915
  - lib/coco/options/group.rb
1913
1916
  - lib/coco/options/item.rb
1914
1917
  - lib/coco/options/option.rb