tramway 3.1.2.4 → 3.1.2.5

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: f525c9fd76a1edc7050318ead14629ddf7c4be7d9d53dcc14b61ee04b5563d18
4
- data.tar.gz: 486e418f27e02dae869e2ef625167998e737e0f9ff2a49fa47caa46fe1fc9d74
3
+ metadata.gz: 564610d50165b34a4446cdc570c377a12f9c49587b366c994141a3e2702e289f
4
+ data.tar.gz: beed0c0e6d742dbec6caeaed2fff43c0b6ff62248b804d723dc7b0ce98883b4b
5
5
  SHA512:
6
- metadata.gz: 04111aad9852f77817a6f01f2b9e96f20ed6ffa24ba70f0a9bd9c12f08060aee26d9deba7fedd437fcedae1b7cb27782df2e82da5c629a090993443a05bb5f47
7
- data.tar.gz: 53117c53d931e58338947378b8c0de7cbb8b92d9b66ee7c918e56bea5a3c7d4dff7e62ebb310f1b1b57013a820f3a81dfc046fa89f39fffbe2553e9ccc042aa0
6
+ metadata.gz: 98bfc099a4338a4e114a7591d9721b209077e3a419b83669b5c5fbe5ce21a4c004515a97af9d4a32bf3c5219c426198176c5e413b3633569b68935f623eb9f3f
7
+ data.tar.gz: f7d43d03755310fa2ae39e8d0a89c2c87ff90791843ce7ee899eae5ef518dd06b7830c45a0a861159dcf7378862c3e008ef7411b21b465a19ade3863085880a1
data/README.md CHANGED
@@ -548,6 +548,24 @@ UserDecorator.decorate user # => nil
548
548
 
549
549
  Read [behave_as_ar](https://github.com/Purple-Magic/tramway#behave_as_ar) section
550
550
 
551
+ #### Actions column on index tables
552
+
553
+ When an entity has `update` or `destroy` pages configured, Tramway automatically
554
+ appends an `Actions` column to the end of the entity's index table headers,
555
+ holding the edit/destroy controls for each row.
556
+
557
+ This label is looked up through `I18n` (`tramway.table.actions_header`), so you
558
+ can rename it to your own language by overriding the key in your app's locale
559
+ file:
560
+
561
+ *config/locales/en.yml*
562
+ ```yaml
563
+ en:
564
+ tramway:
565
+ table:
566
+ actions_header: "Actions"
567
+ ```
568
+
551
569
  ### Tramway Form
552
570
 
553
571
  Tramway provides **convenient** form objects for Rails applications. List properties you want to change and the rules in Form classes. No controllers overloading.
@@ -1073,6 +1091,22 @@ details panel. Pass `preview: false` when you want a row without the preview pan
1073
1091
  <% end %>
1074
1092
  ```
1075
1093
 
1094
+ `tramway_cell` also forwards any HTML options to the underlying cell wrapper. Use this when you need per-cell classes,
1095
+ data attributes, ids, or other attributes:
1096
+
1097
+ ```erb
1098
+ <%= tramway_cell class: 'whitespace-nowrap', data: { turbo: false } do %>
1099
+ <%= user.name %>
1100
+ <% end %>
1101
+ ```
1102
+
1103
+ `tramway_header` forwards HTML options to the header wrapper as well. That makes it easy to add ids, classes, or data
1104
+ attributes to the header row:
1105
+
1106
+ ```erb
1107
+ <%= tramway_header headers: ['Name', 'Email'], class: 'sticky top-0', data: { controller: 'table-header' } %>
1108
+ ```
1109
+
1076
1110
  When you render a header you can either pass the `headers:` array, as in the examples above, or render custom header content in
1077
1111
  the block. `tramway_header` uses the length of the `headers` array to build the grid if the array is present.
1078
1112
  If you omit the array and provide custom content, pass the `columns:` argument so the component knows how many grid columns to
@@ -1196,7 +1230,8 @@ Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are
1196
1230
 
1197
1231
  Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by default. Form inputs use hardcoded
1198
1232
  dark shadcn-style classes; Tramway does not render a separate light form theme.
1199
- Checkboxes render dark while unchecked and use the light primary checked state.
1233
+ Checkboxes render with a visible, fixed-size dark unchecked surface and use the light primary checked state.
1234
+ Checkbox controls, checkmarks, and vertically centered label line heights scale together with the form-level `size:` option.
1200
1235
 
1201
1236
  ```erb
1202
1237
  <%= tramway_form_for @user do |f| %>
@@ -388,15 +388,42 @@ class UiCheckbox extends Controller {
388
388
 
389
389
  this.buttonTarget.setAttribute("aria-checked", checked.toString())
390
390
  this.buttonTarget.dataset.state = state
391
- this.buttonTarget.classList.toggle("border-zinc-50", checked)
391
+ this.syncBoxStyle(checked)
392
+ this.buttonTarget.classList.add("border-zinc-50")
392
393
  this.buttonTarget.classList.toggle("bg-zinc-50", checked)
393
394
  this.buttonTarget.classList.toggle("text-zinc-950", checked)
394
- this.buttonTarget.classList.toggle("border-zinc-800", !checked)
395
- this.buttonTarget.classList.toggle("bg-zinc-950", !checked)
395
+ this.buttonTarget.classList.toggle("bg-zinc-900", !checked)
396
396
  this.buttonTarget.classList.toggle("text-zinc-50", !checked)
397
+ this.buttonTarget.classList.remove("border-zinc-800", "bg-zinc-950")
397
398
  this.indicatorTarget.classList.toggle("hidden", !checked)
398
399
  this.buttonTarget.toggleAttribute("disabled", this.inputTarget.disabled)
399
400
  }
401
+
402
+ syncBoxStyle(checked) {
403
+ const size = this.checkboxSize()
404
+
405
+ this.buttonTarget.style.width = size
406
+ this.buttonTarget.style.height = size
407
+ this.buttonTarget.style.minWidth = size
408
+ this.buttonTarget.style.minHeight = size
409
+ this.buttonTarget.style.display = "inline-flex"
410
+ this.buttonTarget.style.alignItems = "center"
411
+ this.buttonTarget.style.justifyContent = "center"
412
+ this.buttonTarget.style.padding = "0"
413
+ this.buttonTarget.style.lineHeight = "1"
414
+ this.buttonTarget.style.boxSizing = "border-box"
415
+ this.buttonTarget.style.border = "1px solid #fafafa"
416
+ this.buttonTarget.style.backgroundColor = checked ? "#fafafa" : "#18181b"
417
+ this.buttonTarget.style.color = checked ? "#09090b" : "#fafafa"
418
+ this.buttonTarget.style.boxShadow = "0 0 0 1px #fafafa"
419
+ }
420
+
421
+ checkboxSize() {
422
+ if (this.buttonTarget.classList.contains("h-4")) return "1rem"
423
+ if (this.buttonTarget.classList.contains("h-6")) return "1.5rem"
424
+
425
+ return "1.25rem"
426
+ }
400
427
  }
401
428
 
402
429
  class Tooltip extends Controller {
@@ -19,7 +19,8 @@ class TailwindComponent < Tramway::BaseComponent
19
19
  file_button: 'text-sm px-3 py-1',
20
20
  submit_button: 'text-sm px-3 py-1',
21
21
  tramway_select_input: 'text-sm px-2 py-1 h-10',
22
- checkbox_input: 'min-h-4 min-w-4'
22
+ checkbox_input: 'h-4 w-4',
23
+ checkbox_indicator: 'h-3 w-3'
23
24
  },
24
25
  medium: {
25
26
  text_input: 'text-base px-3 py-2',
@@ -27,7 +28,8 @@ class TailwindComponent < Tramway::BaseComponent
27
28
  file_button: 'text-base px-4 py-2',
28
29
  submit_button: 'text-base px-4 py-2',
29
30
  tramway_select_input: 'text-base px-2 py-1 h-12',
30
- checkbox_input: 'min-h-5 min-w-5'
31
+ checkbox_input: 'h-5 w-5',
32
+ checkbox_indicator: 'h-4 w-4'
31
33
  },
32
34
  large: {
33
35
  text_input: 'text-xl px-4 py-3',
@@ -35,7 +37,8 @@ class TailwindComponent < Tramway::BaseComponent
35
37
  file_button: 'text-xl px-5 py-3',
36
38
  submit_button: 'text-xl px-5 py-3',
37
39
  tramway_select_input: 'text-xl px-3 py-2 h-15',
38
- checkbox_input: 'min-h-6 min-w-6'
40
+ checkbox_input: 'h-6 w-6',
41
+ checkbox_indicator: 'h-5 w-5'
39
42
  }
40
43
  }.freeze
41
44
 
@@ -1,4 +1,4 @@
1
- .flex.items-center.space-x-2{ class: default_container_classes, data: { controller: 'ui--checkbox' } }
1
+ .flex.items-center.gap-2{ class: default_container_classes, data: { controller: 'ui--checkbox' } }
2
2
  - checkbox_checked = checked?
3
3
  = @input.call @attribute, **hidden_checkbox_options
4
4
  %button{ type: 'button',
@@ -9,6 +9,7 @@
9
9
  'data-ui--checkbox-target' => 'button',
10
10
  value: 'on',
11
11
  class: checkbox_button_classes,
12
+ style: checkbox_button_style,
12
13
  for: @for,
13
14
  id: @for }
14
15
  %span{ class: checkbox_indicator_classes, style: 'pointer-events: none', data: { 'ui--checkbox-target' => 'indicator' } }
@@ -21,9 +22,8 @@
21
22
  'stroke-width' => '2',
22
23
  'stroke-linecap' => 'round',
23
24
  'stroke-linejoin' => 'round',
24
- class: 'h-4 w-4' }
25
+ class: size_class(:checkbox_indicator) }
25
26
  %polyline{ points: '20 6 9 17 4 12' }
26
27
  - if @label
27
- %div
28
- = component('tramway/form/label', for: @for, options: { class: label_classes, data: { action: 'click->ui--checkbox#toggle' } }) do
29
- = @label
28
+ = component('tramway/form/label', for: @for, options: { class: label_classes, style: label_style, data: { action: 'click->ui--checkbox#toggle' } }) do
29
+ = @label
@@ -4,8 +4,25 @@ module Tramway
4
4
  module Form
5
5
  # Tailwind-styled checkbox field
6
6
  class CheckboxComponent < TailwindComponent
7
+ CHECKBOX_BUTTON_BASE_STYLES = {
8
+ display: 'inline-flex',
9
+ 'align-items': 'center',
10
+ 'justify-content': 'center',
11
+ padding: '0',
12
+ 'line-height': '1',
13
+ 'box-sizing': 'border-box',
14
+ border: '1px solid #fafafa',
15
+ 'box-shadow': '0 0 0 1px #fafafa'
16
+ }.freeze
17
+
18
+ CHECKBOX_BUTTON_SIZES = {
19
+ small: '1rem',
20
+ medium: '1.25rem',
21
+ large: '1.5rem'
22
+ }.freeze
23
+
7
24
  def checkbox_button_classes
8
- 'peer h-4 w-4 shrink-0 rounded-sm border border-zinc-800 bg-zinc-950 text-zinc-50 ' \
25
+ "peer #{size_class(:checkbox_input)} shrink-0 rounded-sm border border-zinc-50 bg-zinc-900 text-zinc-50 " \
9
26
  'ring-offset-zinc-950 ' \
10
27
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-300 ' \
11
28
  'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ' \
@@ -14,7 +31,11 @@ module Tramway
14
31
  end
15
32
 
16
33
  def checkbox_indicator_classes
17
- 'flex items-center justify-center text-current hidden'
34
+ "flex hidden #{size_class(:checkbox_indicator)} items-center justify-center text-current"
35
+ end
36
+
37
+ def checkbox_button_style
38
+ checkbox_button_style_attributes.map { |key, value| "#{key}: #{value}" }.join('; ')
18
39
  end
19
40
 
20
41
  def checked?
@@ -36,19 +57,44 @@ module Tramway
36
57
  end
37
58
 
38
59
  def label_classes
39
- default_classes = 'cursor-pointer mb-0 leading-6'
60
+ default_classes = 'cursor-pointer mb-0'
40
61
 
41
62
  case size
42
63
  when :small
43
- default_classes += ' text-sm'
64
+ default_classes += ' text-sm leading-5'
44
65
  when :medium
45
- default_classes += ' text-base'
66
+ default_classes += ' text-base leading-6'
46
67
  when :large
47
- default_classes += ' text-lg'
68
+ default_classes += ' text-lg leading-7'
48
69
  end
49
70
 
50
71
  default_classes
51
72
  end
73
+
74
+ def label_style
75
+ {
76
+ 'align-self': 'center',
77
+ 'line-height': checkbox_button_size,
78
+ 'margin-bottom': '0'
79
+ }.map { |key, value| "#{key}: #{value}" }.join('; ')
80
+ end
81
+
82
+ private
83
+
84
+ def checkbox_button_style_attributes
85
+ CHECKBOX_BUTTON_BASE_STYLES.merge(
86
+ width: checkbox_button_size,
87
+ height: checkbox_button_size,
88
+ 'min-width': checkbox_button_size,
89
+ 'min-height': checkbox_button_size,
90
+ 'background-color': checked? ? '#fafafa' : '#18181b',
91
+ color: checked? ? '#09090b' : '#fafafa'
92
+ )
93
+ end
94
+
95
+ def checkbox_button_size
96
+ CHECKBOX_BUTTON_SIZES.fetch(size, CHECKBOX_BUTTON_SIZES.fetch(:medium))
97
+ end
52
98
  end
53
99
  end
54
100
  end
@@ -1,2 +1,2 @@
1
- %div{ class: cell_classes }
1
+ %div{ class: [options[:class], cell_classes].compact.join(' '), **options.except(:class) }
2
2
  = content
@@ -4,6 +4,8 @@ module Tramway
4
4
  module Table
5
5
  # Component for rendering a cell in a table
6
6
  class CellComponent < Tramway::BaseComponent
7
+ option :options, optional: true, default: -> { {} }
8
+
7
9
  def cell_classes
8
10
  'div-table-cell hidden px-6 py-4 text-base font-medium text-zinc-100 first:block md:block'
9
11
  end
@@ -1,5 +1,5 @@
1
1
  - parsed_cells = headers.any? ? nil : visible_cells_from(content)
2
- %div{ class: "#{header_row_classes} md:grid-cols-#{columns_count(parsed_cells: parsed_cells)}", aria: { label: "Table Header" }, role: "row" }
2
+ %div{ class: [options[:class], "#{header_row_classes} md:grid-cols-#{columns_count(parsed_cells: parsed_cells)}"].compact.join(' '), aria: { label: "Table Header" }, role: "row", **options.except(:class) }
3
3
  - if headers.any?
4
4
  - headers.each do |header|
5
5
  .div-table-cell.border-b.border-zinc-800{ class: header_cell_classes }
@@ -8,6 +8,7 @@ module Tramway
8
8
 
9
9
  option :headers, optional: true, default: -> { [] }
10
10
  option :columns, optional: true, default: -> { 3 }
11
+ option :options, optional: true, default: -> { {} }
11
12
 
12
13
  def columns_count(content = nil, parsed_cells: nil)
13
14
  return headers.size if headers.present?
@@ -5,6 +5,8 @@ en:
5
5
  title: "Create %{model_name}"
6
6
  edit:
7
7
  title: "Edit %{model_name}"
8
+ table:
9
+ actions_header: "Actions"
8
10
  actions:
9
11
  new: "New"
10
12
  edit: "Edit"
@@ -516,8 +516,12 @@ module.exports = {
516
516
  'leading-none',
517
517
  'appearance-none',
518
518
  'peer',
519
+ 'h-3',
520
+ 'w-3',
519
521
  'h-4',
520
522
  'w-4',
523
+ 'h-6',
524
+ 'w-6',
521
525
  'text-current',
522
526
  'ring-offset-zinc-950',
523
527
  'data-[state=checked]:border-zinc-50',
@@ -578,6 +582,7 @@ module.exports = {
578
582
  'text-sm',
579
583
  'font-medium',
580
584
  'leading-6',
585
+ 'leading-7',
581
586
  'text-zinc-50',
582
587
  'text-green-400',
583
588
  'text-blue-400',
@@ -68,7 +68,9 @@ module Tramway
68
68
  (model_class || entity.model_class).human_attribute_name(attribute)
69
69
  end
70
70
 
71
- headers += ['Actions'] if entity&.page(:update).present? || entity&.page(:destroy).present?
71
+ if entity&.page(:update).present? || entity&.page(:destroy).present?
72
+ headers += [I18n.t('tramway.table.actions_header')]
73
+ end
72
74
 
73
75
  headers
74
76
  end
@@ -23,10 +23,11 @@ module Tramway
23
23
  component 'tramway/table', options:, &
24
24
  end
25
25
 
26
- def tramway_header(headers: nil, columns: nil, &)
26
+ def tramway_header(headers: nil, columns: nil, **options, &)
27
27
  component 'tramway/table/header',
28
28
  headers:,
29
29
  columns:,
30
+ options:,
30
31
  &
31
32
  end
32
33
 
@@ -39,8 +40,8 @@ module Tramway
39
40
  &
40
41
  end
41
42
 
42
- def tramway_cell(&)
43
- component 'tramway/table/cell', &
43
+ def tramway_cell(**options, &)
44
+ component 'tramway/table/cell', options:, &
44
45
  end
45
46
 
46
47
  def tramway_button(path: nil, text: nil, method: :get, form_options: {}, **options, &)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '3.1.2.4'
4
+ VERSION = '3.1.2.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tramway
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2.4
4
+ version: 3.1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - kalashnikovisme
@@ -325,7 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
325
325
  - !ruby/object:Gem::Version
326
326
  version: '0'
327
327
  requirements: []
328
- rubygems_version: 4.0.10
328
+ rubygems_version: 4.0.16
329
329
  specification_version: 4
330
330
  summary: Tramway Rails Engine
331
331
  test_files: []