anchor_view_components 0.9.2 → 0.9.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: 690f9aa0026033a9a42653fcebdd8cac19573199c1a738961d1e0e99637a211b
4
- data.tar.gz: 8c2287d823d21ddb99d766bcc8feda6204ae7807104cca805564c79692bf6efc
3
+ metadata.gz: fc1868d9abc000e67ae9aa10f38a2d462cdcbf2894f96f76ef9f400f899f633c
4
+ data.tar.gz: 9c43abbcfc0e5e746878c90c6379ad3ad5496c26d90fbb0f2723bf581d8c2404
5
5
  SHA512:
6
- metadata.gz: d35cd7a26d617a935c89d0817b231ee6f623e7abfab537cccec147d867b7710c6fc409d0b49c463458f25adbe3a829dd6912653f9d49bbaf60600e6707a4391d
7
- data.tar.gz: 137e7360312efa49b781297351a49d77b7e5d8b5e27117e9f5d5197fd375159f89bffb18ac48d220a5bc7934bec0a6755dfd47e185c569028aaec318c135e0b5
6
+ metadata.gz: 6f1201868d3abb571db947c49f8518a5bdee54b610a3c1282fe983e9deaa780ce24280c42fe514266e9640cc9135a6e8d1889aa99119a302e846bbc664ac24e1
7
+ data.tar.gz: 7cb9fdec805731bace8256159511575c9c36e525f26253253f5c849143784ea30636bf6921db368531db8e76a24c2be88f8c504d24001c06220cebe31a1e624b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.3 - 2023-08-28
4
+
5
+ ### Added
6
+
7
+ - Add TableComponent and helper
8
+ - Introduce `Invoker` Stimulus controller
9
+
3
10
  ## 0.9.2 - 2023-08-25
4
11
 
5
12
  ### Fixed
data/README.md CHANGED
@@ -48,7 +48,10 @@
48
48
 
49
49
  ## Development
50
50
 
51
- TODO
51
+ To see component previews:
52
+
53
+ - Run `yarn build:css --watch` in the root of the project
54
+ - Run `bin/dev` in the `demo/` directory
52
55
 
53
56
  ## License
54
57
 
@@ -0,0 +1,3 @@
1
+ <svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M3 5h18M3 12h18M3 19h18" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ </svg>
@@ -3,14 +3,18 @@ import "@oddbird/popover-polyfill";
3
3
  import ActionMenuController from "./action_menu_controller";
4
4
  import AutocompleteController from "./autocomplete_controller";
5
5
  import DialogController from "./dialog_controller";
6
+ import InvokerController from "./invoker_controller";
6
7
  import ToastController from "./toast_controller";
7
8
  import ToggleController from "./toggle_controller";
9
+ import Sortable from "stimulus-sortable";
8
10
  import { Application } from "@hotwired/stimulus";
9
11
 
10
12
  export function registerAnchorControllers(application: Application) {
11
13
  application.register("action-menu", ActionMenuController);
12
14
  application.register("autocomplete", AutocompleteController);
13
15
  application.register("dialog", DialogController);
16
+ application.register("invoker", InvokerController);
17
+ application.register("sortable", Sortable)
14
18
  application.register("toast", ToastController);
15
19
  application.register("toggle", ToggleController);
16
20
  }
@@ -19,6 +23,7 @@ export {
19
23
  ActionMenuController,
20
24
  AutocompleteController,
21
25
  DialogController,
26
+ InvokerController,
22
27
  ToastController,
23
28
  ToggleController
24
29
  };
@@ -1,45 +1,43 @@
1
- <%= tag.div(
2
- class: "contents",
3
- data: { controller: "dialog" },
1
+ <%= show_button if show_button? %>
2
+
3
+ <%= tag.dialog(
4
+ aria: { labelledby: title_id },
5
+ class: "w-[36rem] rounded-lg p-0 bg-white shadow-lg backdrop:bg-grey-100/50",
6
+ data: {
7
+ controller: "dialog",
8
+ testid: title_id,
9
+ },
4
10
  id: id,
5
11
  ) do %>
6
- <%= show_button if show_button? %>
7
-
8
- <%= tag.dialog(
9
- aria: { labelledby: title_id },
10
- class: "w-[36rem] rounded-lg p-0 bg-white shadow-lg backdrop:bg-grey-100/50",
11
- data: { dialog_target: "dialog", testid: title_id },
12
- ) do %>
13
- <header class="p-6 flex gap-4 justify-between items-center">
14
- <%= render Anchor::TextComponent.new(
15
- variant: :heading_2xl,
16
- tag: :h1,
17
- id: title_id,
18
- data: { testid: "#{title_id}-title" }
19
- ).with_content(title) %>
12
+ <header class="p-6 flex gap-4 justify-between items-center">
13
+ <%= render Anchor::TextComponent.new(
14
+ variant: :heading_2xl,
15
+ tag: :h1,
16
+ id: title_id,
17
+ data: { testid: "#{title_id}-title" }
18
+ ).with_content(title) %>
20
19
 
21
- <form class="contents" method="dialog">
22
- <%= tag.button(
23
- aria: { label: "Close" },
24
- class: "text-grey-50 hover:text-grey-70",
25
- data: { action: "dialog#close" },
26
- ) do %>
27
- <%= render Anchor::IconComponent.new(icon: "cancel") %>
28
- <% end %>
29
- </form>
30
- </header>
20
+ <form class="contents" method="dialog">
21
+ <%= tag.button(
22
+ aria: { label: "Close" },
23
+ class: "text-grey-50 hover:text-grey-70",
24
+ data: { action: "dialog#close" },
25
+ ) do %>
26
+ <%= render Anchor::IconComponent.new(icon: "cancel") %>
27
+ <% end %>
28
+ </form>
29
+ </header>
31
30
 
32
- <div class="px-6 pb-6">
33
- <%= render(Anchor::TextComponent.new(
34
- variant: :body_base,
35
- data: { testid: "#{title_id}-body" }
36
- ).with_content(body)) %>
37
- </div>
31
+ <div class="px-6 pb-6">
32
+ <%= render(Anchor::TextComponent.new(
33
+ variant: :body_base,
34
+ data: { testid: "#{title_id}-body" }
35
+ ).with_content(body)) %>
36
+ </div>
38
37
 
39
- <% if footer? %>
40
- <footer class="sticky bottom-0 bg-white px-6 pb-6 flex gap-2 justify-end">
41
- <%= footer %>
42
- </footer>
43
- <% end %>
38
+ <% if footer? %>
39
+ <footer class="sticky bottom-0 bg-white px-6 pb-6 flex gap-2 justify-end">
40
+ <%= footer %>
41
+ </footer>
44
42
  <% end %>
45
43
  <% end %>
@@ -2,15 +2,20 @@ module Anchor
2
2
  class DialogComponent < Component
3
3
  renders_one :show_button, ->(**kwargs) do
4
4
  ButtonComponent.new(
5
- data: { action: "dialog#showModal",
6
- testid: "#{title.parameterize}-btn" },
5
+ data: {
6
+ action: "invoker#openDialog",
7
+ controller: "invoker",
8
+ invoker_dialog_outlet: "##{id}",
9
+ testid: "#{title.parameterize}-btn",
10
+ },
7
11
  **kwargs
8
12
  )
9
13
  end
10
14
  renders_one :body
11
15
  renders_one :footer
12
16
 
13
- def initialize(title:, **kwargs)
17
+ def initialize(id:, title:, **kwargs)
18
+ @id = id
14
19
  @title = title
15
20
 
16
21
  super
@@ -18,7 +23,7 @@ module Anchor
18
23
 
19
24
  private
20
25
 
21
- attr_reader :title
26
+ attr_reader :id, :title
22
27
 
23
28
  def render?
24
29
  body?
@@ -1,15 +1,11 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
- export default class extends Controller<HTMLDivElement> {
4
- static targets = ["dialog"];
5
-
6
- declare readonly dialogTarget: HTMLDialogElement;
7
-
3
+ export default class extends Controller<HTMLDialogElement> {
8
4
  showModal(): void {
9
- this.dialogTarget.showModal();
5
+ this.element.showModal();
10
6
  }
11
7
 
12
8
  close(): void {
13
- this.dialogTarget.close();
9
+ this.element.close();
14
10
  }
15
11
  }
@@ -0,0 +1,12 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import DialogController from "./dialog_controller";
3
+
4
+ export default class extends Controller {
5
+ static outlets = ["dialog"];
6
+
7
+ declare readonly dialogOutlet: DialogController;
8
+
9
+ openDialog(): void {
10
+ this.dialogOutlet.showModal();
11
+ }
12
+ }
@@ -0,0 +1,37 @@
1
+ <table class="w-full text-base text-left">
2
+ <thead>
3
+ <tr class="border-b">
4
+ <% columns.each do |column| %>
5
+ <%= tag.th(
6
+ column.header,
7
+ class: "px-2 py-3 font-semibold",
8
+ scope: "col",
9
+ ) %>
10
+ <% end %>
11
+ </tr>
12
+ </thead>
13
+
14
+ <%= tag.tbody(data: tbody_data) do
15
+ data.each do |model| %>
16
+ <%= tag.tr(
17
+ class: row_classes,
18
+ data: {
19
+ sortable_update_url: sort_url(model),
20
+ testid: "tr-#{model.id}"
21
+ },
22
+ ) do
23
+ columns.each.with_index do |column, index| %>
24
+ <%= tag.td(
25
+ maybe_link(model, column, index, self).to_s.html_safe,
26
+ class: class_names(
27
+ "relative",
28
+ "p-3 pl-8 bg-[url('assets/icons/menu.svg')] bg-[length:16px_16px] bg-[8px_center] bg-no-repeat": sortable? && index.zero?,
29
+ "px-2 py-3": !sortable? || index.positive?,
30
+ ),
31
+ data: { testid: "tr-#{model.id}-td-#{index}" },
32
+ ) %>
33
+ <% end
34
+ end
35
+ end
36
+ end %>
37
+ </table>
@@ -0,0 +1,90 @@
1
+ module Anchor
2
+ class TableComponent < Component
3
+ renders_many :columns, "ColumnComponent"
4
+ renders_one :link
5
+
6
+ attr_reader :data, :sortable
7
+
8
+ def initialize(data:, sortable: false, sort_url: nil)
9
+ @data = data
10
+ @sortable = sortable
11
+ @sort_url = sort_url
12
+
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def row_classes
19
+ row_classes = %w(border-b focus-within:bg-grey-10)
20
+ if link?
21
+ row_classes += %w(hover:bg-grey-10 hover:cursor-pointer)
22
+ end
23
+ if sortable?
24
+ row_classes += %w(hover:cursor-pointer)
25
+ end
26
+ row_classes.join(" ")
27
+ end
28
+
29
+ def maybe_link(model, column, index, view_context)
30
+ value = value_for(model, column)
31
+ if link?
32
+ path = "#{link}#{model.id}"
33
+ if index.zero?
34
+ text = view_context.link_to(value, path)
35
+ else
36
+ text = value
37
+ end
38
+ text + view_context.link_to(
39
+ "",
40
+ path,
41
+ aria: { hidden: true },
42
+ class: "absolute inset-0",
43
+ tabindex: "-1"
44
+ )
45
+ else
46
+ value
47
+ end
48
+ end
49
+
50
+ def value_for(model, column)
51
+ if column.value.respond_to?(:call)
52
+ column.value.call(model)
53
+ else
54
+ model[column.value]
55
+ end
56
+ end
57
+
58
+ def tbody_data
59
+ if sortable?
60
+ { controller: "sortable", testid: "sortable" }
61
+ else
62
+ {}
63
+ end
64
+ end
65
+
66
+ def sort_url(model)
67
+ if sortable?
68
+ @sort_url.call(model)
69
+ end
70
+ end
71
+
72
+ def sortable?
73
+ sortable
74
+ end
75
+
76
+ def render?
77
+ columns.present? && data.present?
78
+ end
79
+
80
+ class ColumnComponent < Anchor::Component
81
+ attr_reader :header, :value
82
+
83
+ def initialize(header:, value:)
84
+ @header = header
85
+ @value = value
86
+ super
87
+ end
88
+ end
89
+ end
90
+ end
@@ -14,6 +14,7 @@ module Anchor
14
14
  prose
15
15
  side_nav
16
16
  tab_bar
17
+ table
17
18
  toast
18
19
  ].freeze
19
20
 
@@ -1,5 +1,5 @@
1
1
  module Anchor
2
2
  module ViewComponents
3
- VERSION = "0.9.2".freeze
3
+ VERSION = "0.9.3".freeze
4
4
  end
5
5
  end
@@ -0,0 +1,23 @@
1
+ <%= render Anchor::ActionMenuComponent.new do |c| %>
2
+ <% c.with_show_button_content("Menu") %>
3
+
4
+ <% c.with_item do %>
5
+ <%= tag.button(
6
+ "Show dialog",
7
+ data: {
8
+ action: "invoker#openDialog",
9
+ controller: "invoker",
10
+ invoker_dialog_outlet: "#my-dialog",
11
+ },
12
+ ) %>
13
+ <% end %>
14
+ <% end %>
15
+
16
+ <%= render Anchor::DialogComponent.new(
17
+ id: "my-dialog",
18
+ title: "Dialog Title",
19
+ ) do |dialog| %>
20
+ <% dialog.with_body do %>
21
+ <%= tag.p "Content" %>
22
+ <% end %>
23
+ <% end %>
@@ -18,5 +18,7 @@ module Anchor
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ def opens_a_dialog; end
21
23
  end
22
24
  end
@@ -0,0 +1,16 @@
1
+ <%= render Anchor::ButtonComponent.new(
2
+ data: {
3
+ action: "invoker#openDialog",
4
+ controller: "invoker",
5
+ invoker_dialog_outlet: "#my-dialog",
6
+ },
7
+ ).with_content("Show dialog") %>
8
+
9
+ <%= render Anchor::DialogComponent.new(
10
+ id: "my-dialog",
11
+ title: "Dialog Title",
12
+ ) do |dialog| %>
13
+ <% dialog.with_body do %>
14
+ <%= tag.p "Content" %>
15
+ <% end %>
16
+ <% end %>
@@ -1,4 +1,5 @@
1
1
  <%= render Anchor::DialogComponent.new(
2
+ id: "my-dialog",
2
3
  title: "Dialog Title",
3
4
  ) do |dialog| %>
4
5
  <% dialog.with_show_button.with_content("Show Dialog") %>
@@ -16,8 +16,16 @@ module Anchor
16
16
 
17
17
  def with_footer; end
18
18
 
19
+ # The Dialog component has a `show_button` slot, but it’s optional and you
20
+ # can instead provide your own button to open the Dialog, as shown in this
21
+ # example.
22
+ def with_custom_show_button; end
23
+
19
24
  def with_link
20
- render Anchor::DialogComponent.new(title: "Dialog Title") do |c|
25
+ render Anchor::DialogComponent.new(
26
+ id: "my-dialog",
27
+ title: "Dialog Title"
28
+ ) do |c|
21
29
  c.with_show_button(classes: "all-unset underline")
22
30
  .with_content("Show Dialog")
23
31
  c.with_body.with_content("Content")
@@ -0,0 +1,48 @@
1
+ module Anchor
2
+ class TableComponentPreview < ViewComponent::Preview
3
+ class MockData
4
+ attr_reader :id, :name
5
+
6
+ def initialize(id, name)
7
+ @id = id
8
+ @name = name
9
+ end
10
+
11
+ def [](key)
12
+ public_send(key)
13
+ end
14
+ end
15
+
16
+ def playground
17
+ render Anchor::TableComponent.new(data:) do |table|
18
+ table.with_column(header: "Id", value: :id)
19
+ table.with_column(header: "Name", value: -> { _1.name.capitalize })
20
+ end
21
+ end
22
+
23
+ def with_link
24
+ render Anchor::TableComponent.new(data:) do |table|
25
+ table.with_link { "#a_link/" }
26
+ table.with_column(header: "Id", value: :id)
27
+ table.with_column(header: "Name", value: -> { _1.name.capitalize })
28
+ end
29
+ end
30
+
31
+ def sortable
32
+ render Anchor::TableComponent.new(
33
+ data:,
34
+ sortable: true,
35
+ sort_url: ->(data) { "/sort/#{data.id}" }
36
+ ) do |table|
37
+ table.with_column(header: "Id", value: :id)
38
+ table.with_column(header: "Name", value: -> { _1.name.capitalize })
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def data
45
+ (1..4).map { MockData.new(_1, "name #{_1}") }
46
+ end
47
+ end
48
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anchor_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Buoy Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-25 00:00:00.000000000 Z
11
+ date: 2023-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -51,6 +51,7 @@ files:
51
51
  - app/assets/images/icons/actions-check-circle-base.svg
52
52
  - app/assets/images/icons/cancel.svg
53
53
  - app/assets/images/icons/fast-left-circle.svg
54
+ - app/assets/images/icons/menu.svg
54
55
  - app/assets/images/icons/nav-arrow-down.svg
55
56
  - app/assets/images/icons/nav-arrow-left.svg
56
57
  - app/assets/images/icons/nav-arrow-right.svg
@@ -82,6 +83,7 @@ files:
82
83
  - app/components/anchor/dialog_controller.ts
83
84
  - app/components/anchor/icon_component.html.erb
84
85
  - app/components/anchor/icon_component.rb
86
+ - app/components/anchor/invoker_controller.ts
85
87
  - app/components/anchor/loading_indicator_component.html.erb
86
88
  - app/components/anchor/loading_indicator_component.rb
87
89
  - app/components/anchor/panel/body_component.html.erb
@@ -101,6 +103,8 @@ files:
101
103
  - app/components/anchor/tab_bar/tab_component.rb
102
104
  - app/components/anchor/tab_bar_component.html.erb
103
105
  - app/components/anchor/tab_bar_component.rb
106
+ - app/components/anchor/table_component.html.erb
107
+ - app/components/anchor/table_component.rb
104
108
  - app/components/anchor/text_component.html.erb
105
109
  - app/components/anchor/text_component.rb
106
110
  - app/components/anchor/toast_component.html.erb
@@ -115,11 +119,13 @@ files:
115
119
  - lib/anchor/view_components/version.rb
116
120
  - lib/anchor_view_components.rb
117
121
  - previews/anchor/action_menu_component_preview.rb
122
+ - previews/anchor/action_menu_component_preview/opens_a_dialog.html.erb
118
123
  - previews/anchor/badge_component_preview.rb
119
124
  - previews/anchor/banner_component_preview.rb
120
125
  - previews/anchor/breadcrumbs_component_preview.rb
121
126
  - previews/anchor/button_component_preview.rb
122
127
  - previews/anchor/dialog_component_preview.rb
128
+ - previews/anchor/dialog_component_preview/with_custom_show_button.html.erb
123
129
  - previews/anchor/dialog_component_preview/with_footer.html.erb
124
130
  - previews/anchor/icon_component_preview.rb
125
131
  - previews/anchor/loading_indicator_component_preview.rb
@@ -128,6 +134,7 @@ files:
128
134
  - previews/anchor/side_nav_component_preview.rb
129
135
  - previews/anchor/side_nav_component_preview/default.html.erb
130
136
  - previews/anchor/tab_bar_component_preview.rb
137
+ - previews/anchor/table_component_preview.rb
131
138
  - previews/anchor/text_component_preview.rb
132
139
  - previews/anchor/toast_component_preview.rb
133
140
  homepage: https://github.com/BuoySoftware/anchor_view_components