view_primitives 0.2.1 → 0.2.3

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: 5d6e218accad9b3ebd803b8818be2c9afe98ee00272d47b511f98dbf4c643d09
4
- data.tar.gz: d27e24bde3d0b273681b71c4a981a8fb49db3ad8a0125a3da5709e0b633be0e7
3
+ metadata.gz: bf3e09110faf5ce30ed81d7760ab59bbf397fcdd1bfd907748e481d2ad31ae82
4
+ data.tar.gz: bfc5afe878bd677f57a02cf6a69169125a081490702f3194cd95113b0a8901ad
5
5
  SHA512:
6
- metadata.gz: efe24edf1051beb9a015086e288b2258277b013c3a7c982bf937f96a5480d39573876039e020944ea1cff088b844d8f3ff0ff0b8a75e322ec419c8a7f7ebc2cd
7
- data.tar.gz: 3cda9d5baac7d8ecd2d845fb1dbed9a48b8e04f7b43211484be7f036958843ba62012478c289cb7c65014d714d1ee5c5538b5608cbff9670ef7133fe97b1d988
6
+ metadata.gz: c3f2433c648728f95e0dceb2f22352866d7e868aff1b302527d6cc16298bd4251dae28a6c2753b91fd67fb4511edf5b6c38d1dc71a3f7ff1384bb65c1c93a55d
7
+ data.tar.gz: 10711ae4e2f413503f34621b3a3c0ffcf77b0ea80e9162fd2a38e2d2265690eaed83d16dccfd84b38d79d4c686f55eabe502e6c661cfb507c1379404cc12849e
data/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.3] - 2026-09-02
11
+
12
+ ### Fixed
13
+
14
+ - **Chart** resolves `var(--chart-1)`-style CSS custom properties to their computed value before handing colors to Chart.js — Canvas 2D's `fillStyle` cannot resolve CSS variables on its own, so every segment rendered black
15
+ - **Chart** uses one color per data point (not per dataset) for `pie`/`doughnut`/`polarArea`, matching Chart.js's expected `backgroundColor` shape for single-dataset charts
16
+ - **Dialog**, **Sheet**, **Drawer**, **Command**, **Menubar**, **Gallery** close on <kbd>Esc</kbd> again — the Stimulus key filter was `keydown.escape`, but Stimulus only recognizes the `esc` alias, so the action silently failed to bind (and threw in the console) on every one of these components
17
+ - `install`/`add`/`update` generators no longer emit `method redefined` warnings under `ruby -w` — `ComponentCopier` defined its `template`/`copy_file` overrides via `define_method` in the `included` hook, which re-ran and redefined the methods when Rails loaded a generator under a second load path
18
+
19
+ ## [0.2.2] - 2026-07-15
20
+
21
+ ### Fixed
22
+
23
+ - `install` generator respects overwrite prompts for `UI::Styles` and the CSS bundle (same behaviour as `add` and `update`)
24
+ - **Calendar** renders a hidden input when `name` is set but no date is selected — fixes form data loss on clear or initial render
25
+ - **TagsInput** no longer double-appends `[]` when `name` already ends with `[]`
26
+ - **TagsInput** preserves pre-selected values that are missing from the current `options` list (edit forms with stale tags)
27
+
10
28
  ## [0.2.1] - 2026-07-15
11
29
 
12
30
  ### Changed
@@ -148,6 +166,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
148
166
  - `view_primitives:add` exits with status 1 on unknown components; prints copy summary
149
167
  - Requires `view_component >= 4.0` and Rails `>= 7.1`
150
168
 
169
+ [0.2.2]: https://github.com/alec-c4/view_primitives/releases/tag/v0.2.2
151
170
  [0.2.1]: https://github.com/alec-c4/view_primitives/releases/tag/v0.2.1
152
171
  [0.2.0]: https://github.com/alec-c4/view_primitives/releases/tag/v0.2.0
153
172
  [0.1.0]: https://github.com/alec-c4/view_primitives/releases/tag/v0.1.0
@@ -46,7 +46,7 @@ module UI
46
46
  class: cn(CONTAINER, @extra_class),
47
47
  data: { slot: "calendar", controller: "calendar", calendar_month_value: @month.iso8601 },
48
48
  **@html_attrs) do
49
- concat hidden_input if @name && @selected
49
+ concat hidden_input if @name
50
50
  concat header_row
51
51
  concat day_of_week_row
52
52
  concat day_grid
@@ -23,8 +23,18 @@ export default class extends Controller {
23
23
 
24
24
  connect() {
25
25
  const { labels, datasets, options = {}, colors } = JSON.parse(this.configValue)
26
- const palette = colors?.length ? colors : DEFAULT_COLORS
26
+ const palette = (colors?.length ? colors : DEFAULT_COLORS).map((c) => this.#resolveColor(c))
27
+ const perSegment = ["pie", "doughnut", "polarArea"].includes(this.typeValue) && datasets.length === 1
27
28
  const coloredDatasets = datasets.map((dataset, index) => {
29
+ if (perSegment) {
30
+ const segmentColors = (dataset.data ?? []).map((_, i) => palette[i % palette.length])
31
+ return {
32
+ ...dataset,
33
+ backgroundColor: dataset.backgroundColor ?? segmentColors,
34
+ borderColor: dataset.borderColor ?? segmentColors
35
+ }
36
+ }
37
+
28
38
  const color = palette[index % palette.length]
29
39
  return {
30
40
  ...dataset,
@@ -39,7 +49,7 @@ export default class extends Controller {
39
49
  options: {
40
50
  responsive: true,
41
51
  maintainAspectRatio: true,
42
- color: "var(--muted-foreground)",
52
+ color: this.#resolveColor("var(--muted-foreground)"),
43
53
  ...options
44
54
  }
45
55
  })
@@ -49,4 +59,14 @@ export default class extends Controller {
49
59
  this.#chart?.destroy()
50
60
  this.#chart = null
51
61
  }
62
+
63
+ // Canvas fillStyle can't resolve CSS custom properties on its own —
64
+ // read the computed value from the DOM before handing it to Chart.js.
65
+ #resolveColor(value) {
66
+ const match = /^var\((--[^),]+)\)$/.exec(value?.trim() ?? "")
67
+ if (!match) return value
68
+
69
+ const resolved = getComputedStyle(document.documentElement).getPropertyValue(match[1]).trim()
70
+ return resolved || value
71
+ }
52
72
  }
@@ -51,7 +51,7 @@ module UI
51
51
  class: cn(DIALOG, @extra_class),
52
52
  role: "dialog",
53
53
  "aria-modal": "true",
54
- data: { action: "keydown.escape@window->command#close" }) {
54
+ data: { action: "keydown.esc@window->command#close" }) {
55
55
  concat content_tag(:div, class: COMMAND) {
56
56
  concat search_bar
57
57
  concat content_tag(:div, class: LIST, data: { command_target: "list" }) {
@@ -40,7 +40,7 @@ module UI
40
40
  role: "dialog",
41
41
  "aria-modal": "true",
42
42
  "aria-label": @title,
43
- data: { action: "keydown.escape@window->dialog#close" }) {
43
+ data: { action: "keydown.esc@window->dialog#close" }) {
44
44
  concat close_button
45
45
  concat header_area
46
46
  concat content_tag(:div, content) unless content.blank?
@@ -36,7 +36,7 @@ module UI
36
36
  role: "dialog",
37
37
  "aria-modal": "true",
38
38
  "aria-label": @title,
39
- data: { action: "keydown.escape@window->dialog#close" }) {
39
+ data: { action: "keydown.esc@window->dialog#close" }) {
40
40
  concat drag_handle
41
41
  concat header_area
42
42
  concat content_tag(:div, content, class: "px-4 pb-6 text-sm")
@@ -40,7 +40,7 @@ module UI
40
40
  attrs = { class: grid_cls }
41
41
  if @lightbox
42
42
  attrs[:data] = { controller: "gallery",
43
- action: "click@document->gallery#closeOnClickOutside keydown.escape@document->gallery#close" }
43
+ action: "click@document->gallery#closeOnClickOutside keydown.esc@document->gallery#close" }
44
44
  end
45
45
  attrs.merge!(@html_attrs)
46
46
 
@@ -23,7 +23,7 @@ module UI
23
23
  class: cn(BAR, @extra_class),
24
24
  data: {
25
25
  controller: "menubar",
26
- action: "click@document->menubar#closeOnClickOutside keydown.escape@document->menubar#closeAll"
26
+ action: "click@document->menubar#closeOnClickOutside keydown.esc@document->menubar#closeAll"
27
27
  },
28
28
  **@html_attrs) do
29
29
  menus.each { |m| concat m }
@@ -48,7 +48,7 @@ module UI
48
48
  role: "dialog",
49
49
  "aria-modal": "true",
50
50
  "aria-label": @title,
51
- data: { action: "keydown.escape@window->dialog#close" }) {
51
+ data: { action: "keydown.esc@window->dialog#close" }) {
52
52
  concat close_button
53
53
  concat header_area
54
54
  concat content_tag(:div, content, class: "flex-1") unless content.blank?
@@ -56,7 +56,7 @@ module UI
56
56
  class: CHIP,
57
57
  data: {"tags-input-target": "chip", "tags-input-value": opt[:value]}) do
58
58
  concat content_tag(:span, opt[:label], class: CHIP_LABEL)
59
- concat tag.input(type: "hidden", name: "#{@name}[]", value: opt[:value])
59
+ concat tag.input(type: "hidden", name: hidden_input_name, value: opt[:value])
60
60
  concat remove_button(opt[:value])
61
61
  end
62
62
  end
@@ -130,7 +130,13 @@ module UI
130
130
  end
131
131
 
132
132
  def selected_options
133
- normalized_options.select { |o| @values.include?(o[:value]) }
133
+ options_by_value = normalized_options.to_h { |o| [o[:value], o] }
134
+ @values.map { |value| options_by_value.fetch(value, {value: value, label: value}) }
135
+ end
136
+
137
+ def hidden_input_name
138
+ name = @name.to_s
139
+ name.end_with?("[]") ? name : "#{name}[]"
134
140
  end
135
141
  end
136
142
  end
@@ -5,22 +5,23 @@ require_relative "components"
5
5
  module ViewPrimitives
6
6
  module Generators
7
7
  module ComponentCopier
8
- def self.included(base)
9
- base.no_tasks do
10
- define_method(:template) do |source, *args, **options, &blk|
11
- destination = args.first || options[:to]
12
- return unless destination.nil? || confirm_overwrite(destination)
13
-
14
- super(source, *args, **options, &blk)
15
- end
8
+ # Wrap Thor::Actions' file helpers so they prompt before clobbering an
9
+ # existing file. Defined as ordinary module methods (not via
10
+ # `define_method` inside `included`) so that re-including the module stays
11
+ # a silent no-op — Rails can load a generator under two different
12
+ # $LOADED_FEATURES keys, which reopens the class and runs `include` again.
13
+ def template(source, *args, **options, &blk)
14
+ destination = args.first || options[:to]
15
+ return unless destination.nil? || confirm_overwrite(destination)
16
+
17
+ super
18
+ end
16
19
 
17
- define_method(:copy_file) do |source, *args, **options|
18
- destination = args.first || options[:to]
19
- return unless destination.nil? || confirm_overwrite(destination)
20
+ def copy_file(source, *args, **options, &blk)
21
+ destination = args.first || options[:to]
22
+ return unless destination.nil? || confirm_overwrite(destination)
20
23
 
21
- super(source, *args, **options)
22
- end
23
- end
24
+ super
24
25
  end
25
26
 
26
27
  private
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../detector"
4
+ require_relative "../component_copier"
4
5
 
5
6
  module ViewPrimitives
6
7
  module Generators
7
8
  class InstallGenerator < Rails::Generators::Base
8
9
  include Detector
10
+ include ComponentCopier
9
11
 
10
12
  source_root File.expand_path("templates", __dir__)
11
13
 
12
14
  class_option :force, type: :boolean, default: false,
13
- desc: "Overwrite existing ApplicationComponent and CSS files"
15
+ desc: "Overwrite existing ApplicationComponent, UI styles, and CSS files"
14
16
 
15
17
  def verify_ui_inflection
16
18
  return if "ui/button_component".camelize == "UI::ButtonComponent"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ViewPrimitives
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_primitives
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Poimtsev
@@ -262,14 +262,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
262
262
  requirements:
263
263
  - - ">="
264
264
  - !ruby/object:Gem::Version
265
- version: 3.2.0
265
+ version: 3.3.0
266
266
  required_rubygems_version: !ruby/object:Gem::Requirement
267
267
  requirements:
268
268
  - - ">="
269
269
  - !ruby/object:Gem::Version
270
270
  version: '0'
271
271
  requirements: []
272
- rubygems_version: 4.0.16
272
+ rubygems_version: 4.0.19
273
273
  specification_version: 4
274
274
  summary: Primitive view components and helpers for Rails applications
275
275
  test_files: []