shadcn_view_components 0.2.0 → 0.2.1

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: 2101ff232a685f61321d32cf4c61610f5c3cf80f1524928a3993ec90415bb006
4
- data.tar.gz: 96b26bbd4b16ba7bbcb813cf07463c988931daaa28eaa6310ebacc4676c9ac93
3
+ metadata.gz: 5a817d07fcea9d79467f32d8ba3a0c2cd58fcafd2fac59664f50963bd099e551
4
+ data.tar.gz: 2c7d7f5b36b0c2ce319a3f159e98c0b685f02467b0c2737a5e2bcb6a39a1fe84
5
5
  SHA512:
6
- metadata.gz: 949f78dcbb1c70d337c939b2e2ec327f44d6c58f5b0fc1e7fdd958f6598a3166849fa65a1c15de82b7dcd975d2a99f02529b0141521120ad5639582750981353
7
- data.tar.gz: e87d59f8726efb40fcea61bc4f9ef41870e40391ee34a4b69bdd1861e904ee4bcc64629e449000ee6feb3e65862b55016a920a5b1f51ddadf0e45957036e3d64
6
+ metadata.gz: 310c43a4aa66d4a2446553e0de95c888f2722bf9d09c6acfebe91f997ea7d25209331480f37f04fcef300f4c4ac00c2ae25f7f9421004635e5c3a1cb4f9b1b06
7
+ data.tar.gz: 46b8be5f13e80117e632d1c464fdb709f8c30fd124446f8939985cdc446add6ed3534633fba1a967b3b593c4463d02c47a49f3cf8eb7bbf27bc165aff86e90b1
@@ -22,6 +22,9 @@ export default class SidebarController extends Controller {
22
22
  )
23
23
  for (const sidebar of sidebars) {
24
24
  sidebar.dataset.state = state
25
+ if (sidebar.dataset.collapsibleMode) {
26
+ sidebar.dataset.collapsible = state === "closed" ? sidebar.dataset.collapsibleMode : ""
27
+ }
25
28
  }
26
29
  for (const trigger of this.element.querySelectorAll("[data-slot='sidebar-trigger']")) {
27
30
  trigger.setAttribute("aria-expanded", String(state === "open"))
@@ -23,6 +23,11 @@ module Shadcn
23
23
  { orientation: @orientation, size: @size }
24
24
  end
25
25
 
26
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
27
+ def contract_data_attributes
28
+ super.merge(orientation: @orientation, size: @size)
29
+ end
30
+
26
31
  class Group < BaseComponent
27
32
  # div
28
33
  end
@@ -39,6 +44,11 @@ module Shadcn
39
44
  def variant_options
40
45
  { variant: @variant }
41
46
  end
47
+
48
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
49
+ def contract_data_attributes
50
+ super.merge(variant: @variant)
51
+ end
42
52
  end
43
53
 
44
54
  class Content < BaseComponent
@@ -58,15 +68,37 @@ module Shadcn
58
68
  end
59
69
 
60
70
  class Action < BaseComponent
61
- # button
62
- sig { override.returns(String) }
63
- def default_tag
64
- "button"
71
+ class << self
72
+ extend T::Sig
73
+
74
+ sig do
75
+ params(extra: T.nilable(String), options: T::Hash[Symbol, T.nilable(T.any(Symbol, String))]).returns(String).checked(:never)
76
+ end
77
+ def classes(extra: nil, **options)
78
+ unknown = options.keys - %i[variant size]
79
+ raise ArgumentError, "unknown variant props: #{unknown.inspect}" unless unknown.empty?
80
+
81
+ variant = options[:variant] || :ghost
82
+ size = options[:size] || :"icon-xs"
83
+ T.unsafe(ShadcnViewComponents::Classes).resolve(:button, extra: extra, variant: variant, size: size)
84
+ end
65
85
  end
66
86
 
67
- sig { override.returns(T::Hash[Symbol, T.untyped]) }
68
- def html_attributes
69
- super.tap { |attributes| attributes[:type] = "button" unless attributes.key?(:type) }
87
+ sig do
88
+ params(variant: T.any(Symbol, String), size: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never)
89
+ end
90
+ def initialize(variant: :ghost, size: :"icon-xs", **args)
91
+ @variant = T.let(ShadcnViewComponents::Classes.normalize_option(:button, :variant, variant), Symbol)
92
+ @size = T.let(ShadcnViewComponents::Classes.normalize_option(:button, :size, size), Symbol)
93
+ super(**args)
94
+ end
95
+
96
+ sig { override.returns(String) }
97
+ def call
98
+ attributes = @html_args.merge(class: @user_class)
99
+ attributes[:type] = "button" unless attributes.key?(:type)
100
+ merge_nested(attributes, :data, { slot: "attachment-action" })
101
+ render(::Shadcn::Button.new(variant: @variant, size: @size, **attributes)) { content }
70
102
  end
71
103
  end
72
104
 
@@ -4,9 +4,11 @@
4
4
  module Shadcn
5
5
  # チャットの吹き出し(Phase 4)
6
6
  class Bubble < BaseComponent
7
- sig { params(variant: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
8
- def initialize(variant: ShadcnViewComponents::Contracts::Bubble::DEFAULTS.fetch(:variant), **args)
7
+ sig { params(variant: T.any(Symbol, String), align: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
8
+ def initialize(variant: ShadcnViewComponents::Contracts::Bubble::DEFAULTS.fetch(:variant),
9
+ align: self.class.property_default(:align), **args)
9
10
  @variant = T.let(normalize_option(:variant, variant), Symbol)
11
+ @align = T.let(normalize_property(:align, align), String)
10
12
  super(**args)
11
13
  end
12
14
 
@@ -15,6 +17,11 @@ module Shadcn
15
17
  { variant: @variant }
16
18
  end
17
19
 
20
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
21
+ def contract_data_attributes
22
+ super.merge(variant: @variant, align: @align)
23
+ end
24
+
18
25
  class Group < BaseComponent
19
26
  # div
20
27
  end
@@ -43,6 +50,11 @@ module Shadcn
43
50
  def variant_options
44
51
  { align: @align, side: @side }
45
52
  end
53
+
54
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
55
+ def contract_data_attributes
56
+ super.merge(align: @align, side: @side)
57
+ end
46
58
  end
47
59
  end
48
60
  end
@@ -15,6 +15,11 @@ module Shadcn
15
15
  { orientation: @orientation }
16
16
  end
17
17
 
18
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
19
+ def contract_data_attributes
20
+ super.merge(orientation: @orientation)
21
+ end
22
+
18
23
  class Separator < BaseComponent
19
24
  # div(契約クラスは upstream の separator 上書き分を事前解決したもの)
20
25
  sig { params(orientation: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
@@ -3,6 +3,17 @@
3
3
 
4
4
  module Shadcn
5
5
  class Card < BaseComponent
6
+ sig { params(size: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
7
+ def initialize(size: self.class.property_default(:size), **args)
8
+ @size = T.let(normalize_property(:size, size), String)
9
+ super(**args)
10
+ end
11
+
12
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
13
+ def contract_data_attributes
14
+ super.merge(size: @size)
15
+ end
16
+
6
17
  class Header < BaseComponent; end
7
18
 
8
19
  class Title < BaseComponent; end
@@ -80,6 +80,20 @@ module Shadcn
80
80
 
81
81
  class Item < BaseComponent
82
82
  # role=menuitem。data-highlighted と tabindex はコントローラが管理する
83
+ sig { params(variant: T.nilable(T.any(Symbol, String)), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
84
+ def initialize(variant: nil, **args)
85
+ @variant = T.let(
86
+ supports_variant? ? normalize_property(:variant, variant || self.class.property_default(:variant)) : nil,
87
+ T.nilable(String)
88
+ )
89
+ super(**args)
90
+ end
91
+
92
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
93
+ def contract_data_attributes
94
+ @variant ? super.merge(variant: @variant) : super
95
+ end
96
+
83
97
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
84
98
  def html_attributes
85
99
  attributes = super
@@ -91,6 +105,12 @@ module Shadcn
91
105
 
92
106
  private
93
107
 
108
+ sig { returns(T::Boolean) }
109
+ def supports_variant?
110
+ slots = T.cast(self.class.contract.const_get(:SLOTS), T::Array[T::Hash[Symbol, T.untyped]])
111
+ slots.any? { |slot| T.cast(slot[:dynamic_attributes], T::Array[String]).include?("data-variant") }
112
+ end
113
+
94
114
  sig { returns(String) }
95
115
  def default_action
96
116
  "#{controller_identifier}#activate"
@@ -106,7 +126,7 @@ module Shadcn
106
126
  sig { params(checked: T::Boolean, args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
107
127
  def initialize(checked: false, **args)
108
128
  @checked = checked
109
- super(**args)
129
+ super(**T.unsafe(args))
110
130
  end
111
131
 
112
132
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
@@ -73,6 +73,17 @@ module Shadcn
73
73
  end
74
74
 
75
75
  class Legend < BaseComponent
76
+ sig { params(variant: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
77
+ def initialize(variant: self.class.property_default(:variant), **args)
78
+ @variant = T.let(normalize_property(:variant, variant), String)
79
+ super(**args)
80
+ end
81
+
82
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
83
+ def contract_data_attributes
84
+ super.merge(variant: @variant)
85
+ end
86
+
76
87
  sig { override.returns(String) }
77
88
  def default_tag
78
89
  "legend"
@@ -7,6 +7,25 @@ module Shadcn
7
7
  # data-slot を差し替える(Phase 3 の AlertDialogAction と同じ構成)。
8
8
  # NOTE: Shadcn::Button の sidecar テンプレートと名前が衝突するため専用ファイルに置く
9
9
  class Button < BaseComponent
10
+ class << self
11
+ extend T::Sig
12
+
13
+ sig do
14
+ params(extra: T.nilable(String), options: T::Hash[Symbol, T.nilable(T.any(Symbol, String))]).returns(String).checked(:never)
15
+ end
16
+ def classes(extra: nil, **options)
17
+ unknown = options.keys - %i[variant size]
18
+ raise ArgumentError, "unknown variant props: #{unknown.inspect}" unless unknown.empty?
19
+
20
+ variant = options[:variant] || :ghost
21
+ size = options[:size] || :xs
22
+ button_variant = ShadcnViewComponents::Classes.normalize_option(:button, :variant, variant)
23
+ group_size = ShadcnViewComponents::Classes.normalize_option(:"input_group/button", :size, size)
24
+ overlay = T.unsafe(ShadcnViewComponents::Classes).resolve(:"input_group/button", extra: extra, size: group_size)
25
+ T.unsafe(ShadcnViewComponents::Classes).resolve(:button, extra: overlay, variant: button_variant)
26
+ end
27
+ end
28
+
10
29
  sig do
11
30
  params(
12
31
  variant: T.any(Symbol, String),
@@ -28,20 +47,17 @@ module Shadcn
28
47
  { size: @size }
29
48
  end
30
49
 
31
- # 契約は Button(ba_buttonVariants) との合成結果を事前解決済みのため、
32
- # ここではその最終クラスをそのまま描く(upstream の DOM と同一)
50
+ # upstream の <Button> が基本クラスと variant を付け、
51
+ # input-group 側の contract がサイズ調整を上書きする。
33
52
  sig { override.returns(String) }
34
- def default_tag
35
- "button"
36
- end
37
-
38
- sig { override.returns(T::Hash[Symbol, T.untyped]) }
39
- def html_attributes
40
- attributes = super
53
+ def call
54
+ # Button に渡すのは InputGroup 固有のクラス。公開 classes は合成済みを返す。
55
+ overlay = T.unsafe(ShadcnViewComponents::Classes).resolve(:"input_group/button", extra: @user_class, size: @size)
56
+ attributes = @html_args.merge(class: overlay)
41
57
  attributes[:type] = "button" unless attributes.key?(:type)
42
58
  data = T.cast(attributes[:data], T.nilable(T::Hash[Symbol, T.untyped])) || {}
43
59
  attributes[:data] = { size: @size }.merge(data)
44
- attributes
60
+ render(::Shadcn::Button.new(variant: @variant, **attributes)) { content }
45
61
  end
46
62
  end
47
63
  end
@@ -4,7 +4,16 @@
4
4
  module Shadcn
5
5
  # AIチャット等のメッセージ行(Phase 4)。静的な表示構造
6
6
  class Message < BaseComponent
7
- # div
7
+ sig { params(align: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
8
+ def initialize(align: self.class.property_default(:align), **args)
9
+ @align = T.let(normalize_property(:align, align), String)
10
+ super(**args)
11
+ end
12
+
13
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
14
+ def contract_data_attributes
15
+ super.merge(align: @align)
16
+ end
8
17
 
9
18
  class Group < BaseComponent
10
19
  # div
@@ -0,0 +1,49 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Shadcn
5
+ class MessageScroller < BaseComponent
6
+ # Button の sidecar テンプレートとの名前の衝突を避けるため専用ファイルに置く。
7
+ class Button < BaseComponent
8
+ class << self
9
+ extend T::Sig
10
+
11
+ sig do
12
+ params(extra: T.nilable(String), options: T::Hash[Symbol, T.nilable(T.any(Symbol, String))]).returns(String).checked(:never)
13
+ end
14
+ def classes(extra: nil, **options)
15
+ unknown = options.keys - %i[variant size]
16
+ raise ArgumentError, "unknown variant props: #{unknown.inspect}" unless unknown.empty?
17
+
18
+ variant = options[:variant] || :secondary
19
+ size = options[:size] || :"icon-sm"
20
+ overlay = ShadcnViewComponents::Classes.resolve(:"message_scroller/button", extra: extra)
21
+ T.unsafe(ShadcnViewComponents::Classes).resolve(:button, extra: overlay, variant: variant, size: size)
22
+ end
23
+ end
24
+
25
+ sig do
26
+ params(
27
+ variant: T.any(Symbol, String), size: T.any(Symbol, String),
28
+ direction: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]
29
+ ).void.checked(:never)
30
+ end
31
+ def initialize(variant: :secondary, size: :"icon-sm", direction: self.class.property_default(:direction), **args)
32
+ @variant = T.let(ShadcnViewComponents::Classes.normalize_option(:button, :variant, variant), Symbol)
33
+ @size = T.let(ShadcnViewComponents::Classes.normalize_option(:button, :size, size), Symbol)
34
+ @direction = T.let(normalize_property(:direction, direction), String)
35
+ super(**args)
36
+ end
37
+
38
+ sig { override.returns(String) }
39
+ def call
40
+ attributes = @html_args.merge(class: ShadcnViewComponents::Classes.resolve(:"message_scroller/button", extra: @user_class))
41
+ attributes[:type] = "button" unless attributes.key?(:type)
42
+ merge_nested(attributes, :data, { slot: "message-scroller-button", direction: @direction,
43
+ variant: @variant, size: @size,
44
+ action: "#{CONTROLLER}#scrollToBottom" })
45
+ render(::Shadcn::Button.new(variant: @variant, size: @size, **attributes)) { content }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -32,21 +32,5 @@ module Shadcn
32
32
  class Item < BaseComponent
33
33
  # div
34
34
  end
35
-
36
- class Button < BaseComponent
37
- # 一番下へ戻るボタン
38
- sig { override.returns(String) }
39
- def default_tag
40
- "button"
41
- end
42
-
43
- sig { override.returns(T::Hash[Symbol, T.untyped]) }
44
- def html_attributes
45
- attributes = super
46
- attributes[:type] = "button" unless attributes.key?(:type)
47
- merge_nested(attributes, :data, { action: "#{CONTROLLER}#scrollToBottom" })
48
- attributes
49
- end
50
- end
51
35
  end
52
36
  end
@@ -10,15 +10,25 @@ module Shadcn
10
10
  class Sidebar < BaseComponent
11
11
  CONTROLLER = "shadcn--sidebar"
12
12
 
13
- sig { params(state: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never) }
14
- def initialize(state: self.class.property_default(:state), **args)
13
+ sig do
14
+ params(state: T.any(Symbol, String), side: T.any(Symbol, String), variant: T.any(Symbol, String),
15
+ collapsible: T.any(Symbol, String), args: T::Hash[Symbol, T.untyped]).void.checked(:never)
16
+ end
17
+ def initialize(state: self.class.property_default(:state), side: self.class.property_default(:side),
18
+ variant: self.class.property_default(:variant),
19
+ collapsible: self.class.property_default(:collapsible), **args)
15
20
  @state = T.let(normalize_property(:state, state), String)
21
+ @side = T.let(normalize_property(:side, side), String)
22
+ @variant = T.let(normalize_property(:variant, variant), String)
23
+ @collapsible = T.let(normalize_property(:collapsible, collapsible), String)
16
24
  super(**args)
17
25
  end
18
26
 
19
27
  # 契約スロット構成: sidebar(状態root) > sidebar-gap / sidebar-container > sidebar-inner
20
28
  sig { override.returns(String) }
21
29
  def call
30
+ return content_tag(:div, **html_attributes) { content } if @collapsible == "none"
31
+
22
32
  content_tag(:div, **html_attributes) do
23
33
  safe_join([gap, container])
24
34
  end
@@ -26,9 +36,20 @@ module Shadcn
26
36
 
27
37
  sig { override.returns(T::Hash[Symbol, T.untyped]) }
28
38
  def html_attributes
29
- attributes = @html_args.merge(class: self.class.classes(extra: @user_class))
39
+ root_class = if @collapsible == "none"
40
+ self.class.classes(extra: @user_class)
41
+ else
42
+ T.cast(desktop_root_slot.dig(:static_attributes, :class), String)
43
+ end
44
+ attributes = @html_args.merge(class: root_class)
30
45
  data = T.cast(attributes[:data], T.nilable(T::Hash[Symbol, T.untyped])) || {}
31
- attributes[:data] = { slot: "sidebar", state: @state }.merge(data)
46
+ attributes[:data] = if @collapsible == "none"
47
+ { slot: "sidebar", state: @state }.merge(data)
48
+ else
49
+ { slot: "sidebar", state: @state, side: @side, variant: @variant,
50
+ collapsible: @state == "closed" ? @collapsible : "",
51
+ collapsible_mode: @collapsible }.merge(data)
52
+ end
32
53
  attributes
33
54
  end
34
55
 
@@ -41,14 +62,26 @@ module Shadcn
41
62
 
42
63
  sig { returns(String) }
43
64
  def container
44
- content_tag(:div, class: slot_class("sidebar-container"), data: { slot: "sidebar-container" }) do
65
+ container_class = slot_class("sidebar-container")
66
+ container_class = ShadcnViewComponents::Classes::MERGER.merge("#{container_class} #{@user_class}") if @user_class
67
+ content_tag(:div, class: container_class, data: { slot: "sidebar-container", side: @side }) do
45
68
  content_tag(:div, class: slot_class("sidebar-inner"), data: { slot: "sidebar-inner" }) { content }
46
69
  end
47
70
  end
48
71
 
49
72
  sig { params(name: String).returns(T.nilable(String)) }
50
73
  def slot_class(name)
51
- T.cast(contract_slot(name).dig(:static_attributes, :class), T.nilable(String))
74
+ slot = contract_slot(name)
75
+ variants = T.cast(slot[:class_variants], T.nilable(T::Hash[Symbol, T::Hash[Symbol, String]]))
76
+ return variants.fetch(:variant).fetch(@variant.to_sym) if variants
77
+
78
+ T.cast(slot.dig(:static_attributes, :class), T.nilable(String))
79
+ end
80
+
81
+ sig { returns(T::Hash[Symbol, T.untyped]) }
82
+ def desktop_root_slot
83
+ slots = T.cast(contract.const_get(:SLOTS), T::Array[T::Hash[Symbol, T.untyped]])
84
+ slots.find { |slot| slot[:name] == "sidebar" && slot[:tag] == "div" && slot.dig(:static_attributes, :class) } || {}
52
85
  end
53
86
 
54
87
  class Provider < BaseComponent
@@ -69,6 +102,19 @@ module Shadcn
69
102
  end
70
103
 
71
104
  class Trigger < BaseComponent
105
+ class << self
106
+ extend T::Sig
107
+
108
+ sig { params(extra: T.nilable(String), options: T::Hash[Symbol, T.nilable(T.any(Symbol, String))]).returns(String).checked(:never) }
109
+ def classes(extra: nil, **options)
110
+ raise ArgumentError, "unknown variant props: #{options.keys.inspect}" unless options.empty?
111
+
112
+ T.unsafe(ShadcnViewComponents::Classes).resolve(
113
+ :button, extra: extra, variant: :ghost, size: :"icon-sm"
114
+ )
115
+ end
116
+ end
117
+
72
118
  sig { override.returns(String) }
73
119
  def default_tag
74
120
  "button"
@@ -230,7 +276,7 @@ module Shadcn
230
276
  attributes = super
231
277
  attributes[:type] = "button" unless attributes.key?(:type) || tag == "a"
232
278
  # 契約スロットの静的属性 data-sidebar="menu-button"
233
- merge_nested(attributes, :data, { sidebar: "menu-button", active: @active.to_s })
279
+ merge_nested(attributes, :data, { sidebar: "menu-button", active: @active.to_s, size: @size })
234
280
  attributes
235
281
  end
236
282
  end
@@ -53,6 +53,17 @@ module Shadcn
53
53
  attributes
54
54
  end
55
55
 
56
+ sig { override.returns(String) }
57
+ def call
58
+ previous = ActiveSupport::IsolatedExecutionState[:shadcn_toggle_group]
59
+ ActiveSupport::IsolatedExecutionState[:shadcn_toggle_group] = {
60
+ variant: @group_variant, size: @group_size, spacing: @spacing
61
+ }
62
+ super
63
+ ensure
64
+ ActiveSupport::IsolatedExecutionState[:shadcn_toggle_group] = previous
65
+ end
66
+
56
67
  private
57
68
 
58
69
  # グループの variant/size を Item の許容値で fail-fast 検証する
@@ -69,9 +80,7 @@ module Shadcn
69
80
  "button"
70
81
  end
71
82
 
72
- # variant/size はupstreamではグループのcontextから受け取る。Ruby側では
73
- # 利用者が各Itemに指定する(規約: 省略時は契約の既定値)。
74
- # spacing はグループと同じ既定値2を使い、data属性に出力する
83
+ # 親の描画スコープ内では upstream の context と同じ優先順位で値を受け取る。
75
84
  sig do
76
85
  params(
77
86
  variant: T.any(Symbol, String),
@@ -100,7 +109,7 @@ module Shadcn
100
109
 
101
110
  sig { override.returns(T::Hash[Symbol, VariantOption]) }
102
111
  def variant_options
103
- { variant: @variant, size: @size }
112
+ { variant: effective_variant, size: effective_size }
104
113
  end
105
114
 
106
115
  # upstream の Item と同じ data 属性(spacing の角丸・枠線制御はここから効く)
@@ -108,9 +117,9 @@ module Shadcn
108
117
  def contract_data_attributes
109
118
  super.merge(
110
119
  state: @state,
111
- variant: @variant,
112
- size: @size,
113
- spacing: @spacing.to_s,
120
+ variant: effective_variant,
121
+ size: effective_size,
122
+ spacing: effective_spacing.to_s,
114
123
  action: "#{ToggleGroup::CONTROLLER}#toggleItem"
115
124
  )
116
125
  end
@@ -122,6 +131,28 @@ module Shadcn
122
131
  attributes[:type] = "button" unless @html_args.key?(:type) || attributes.key?(:type)
123
132
  attributes
124
133
  end
134
+
135
+ private
136
+
137
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
138
+ def group_context
139
+ T.cast(ActiveSupport::IsolatedExecutionState[:shadcn_toggle_group], T.nilable(T::Hash[Symbol, T.untyped]))
140
+ end
141
+
142
+ sig { returns(Symbol) }
143
+ def effective_variant
144
+ T.cast(group_context&.dig(:variant) || @variant, Symbol)
145
+ end
146
+
147
+ sig { returns(Symbol) }
148
+ def effective_size
149
+ T.cast(group_context&.dig(:size) || @size, Symbol)
150
+ end
151
+
152
+ sig { returns(T.any(Integer, Float)) }
153
+ def effective_spacing
154
+ T.cast(group_context&.dig(:spacing) || @spacing, T.any(Integer, Float))
155
+ end
125
156
  end
126
157
  end
127
158
  end
@@ -23,8 +23,8 @@ module ShadcnViewComponents
23
23
  { name: "sidebar", tag: "div", static_attributes: {}.freeze, dynamic_attributes: [].freeze }.freeze,
24
24
  { name: "sidebar", tag: "SheetContent", static_attributes: { class: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden", "data-mobile" => "true", "data-sidebar" => "sidebar" }.freeze, dynamic_attributes: ["dir", "side", "style"].freeze }.freeze,
25
25
  { name: "sidebar", tag: "div", static_attributes: { class: "group peer hidden text-sidebar-foreground md:block" }.freeze, dynamic_attributes: ["data-collapsible", "data-side", "data-state", "data-variant"].freeze }.freeze,
26
- { name: "sidebar-gap", tag: "div", static_attributes: { class: "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-(--sidebar-width-icon)" }.freeze, dynamic_attributes: [].freeze }.freeze,
27
- { name: "sidebar-container", tag: "div", static_attributes: { class: "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l" }.freeze, dynamic_attributes: ["data-side"].freeze }.freeze,
26
+ { name: "sidebar-gap", tag: "div", static_attributes: { class: "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-(--sidebar-width-icon)" }.freeze, dynamic_attributes: [].freeze, class_variants: { variant: { floating: "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]", inset: "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]", sidebar: "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-(--sidebar-width-icon)" }.freeze }.freeze }.freeze,
27
+ { name: "sidebar-container", tag: "div", static_attributes: { class: "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l" }.freeze, dynamic_attributes: ["data-side"].freeze, class_variants: { variant: { floating: "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]", inset: "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]", sidebar: "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l" }.freeze }.freeze }.freeze,
28
28
  { name: "sidebar-inner", tag: "div", static_attributes: { class: "flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border", "data-sidebar" => "sidebar" }.freeze, dynamic_attributes: [].freeze }.freeze
29
29
  ].freeze, T::Array[T::Hash[Symbol, T.untyped]])
30
30
 
@@ -2,6 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ShadcnViewComponents
5
+ # 公開propの制約は一箇所で確認できるように集約する。
6
+ # rubocop:disable-next Metrics/ModuleLength
5
7
  module PropertyContracts
6
8
  DIRECTIONS = %w[ltr rtl].freeze
7
9
  ORIENTATIONS = %w[horizontal vertical].freeze
@@ -51,11 +53,38 @@ module ShadcnViewComponents
51
53
  state: { kind: :enum, default: "off", values: TOGGLE_STATES }.freeze,
52
54
  spacing: { kind: :number, default: 2, minimum: 0 }.freeze
53
55
  }.freeze,
56
+ "dropdown_menu/item": {
57
+ variant: { kind: :enum, default: "default", values: %w[default destructive].freeze }.freeze
58
+ }.freeze,
59
+ "context_menu/item": {
60
+ variant: { kind: :enum, default: "default", values: %w[default destructive].freeze }.freeze
61
+ }.freeze,
62
+ "menubar/item": {
63
+ variant: { kind: :enum, default: "default", values: %w[default destructive].freeze }.freeze
64
+ }.freeze,
65
+ "field/legend": {
66
+ variant: { kind: :enum, default: "legend", values: %w[legend label].freeze }.freeze
67
+ }.freeze,
68
+ card: {
69
+ size: { kind: :enum, default: "default", values: %w[default sm].freeze }.freeze
70
+ }.freeze,
71
+ message: {
72
+ align: { kind: :enum, default: "start", values: %w[start end].freeze }.freeze
73
+ }.freeze,
74
+ bubble: {
75
+ align: { kind: :enum, default: "start", values: %w[start end].freeze }.freeze
76
+ }.freeze,
77
+ "message_scroller/button": {
78
+ direction: { kind: :enum, default: "end", values: %w[start end].freeze }.freeze
79
+ }.freeze,
54
80
  direction_provider: {
55
81
  dir: { kind: :enum, default: "ltr", values: DIRECTIONS }.freeze
56
82
  }.freeze,
57
83
  sidebar: {
58
- state: { kind: :enum, default: "open", values: %w[open closed].freeze }.freeze
84
+ state: { kind: :enum, default: "open", values: %w[open closed].freeze }.freeze,
85
+ side: { kind: :enum, default: "left", values: %w[left right].freeze }.freeze,
86
+ variant: { kind: :enum, default: "sidebar", values: %w[sidebar floating inset].freeze }.freeze,
87
+ collapsible: { kind: :enum, default: "offcanvas", values: %w[offcanvas icon none].freeze }.freeze
59
88
  }.freeze,
60
89
  "sidebar/provider": {
61
90
  state: { kind: :enum, default: "open", values: %w[open closed].freeze }.freeze
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ShadcnViewComponents
5
- VERSION = "0.2.0"
5
+ VERSION = "0.2.1"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shadcn_view_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - supermomonga
@@ -320,6 +320,7 @@ files:
320
320
  - app/components/shadcn/menubar.rb
321
321
  - app/components/shadcn/message.rb
322
322
  - app/components/shadcn/message_scroller.rb
323
+ - app/components/shadcn/message_scroller/button.rb
323
324
  - app/components/shadcn/native_checked_state.rb
324
325
  - app/components/shadcn/native_select.rb
325
326
  - app/components/shadcn/navigation_menu.rb