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 +4 -4
- data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +3 -0
- data/app/components/shadcn/attachment.rb +39 -7
- data/app/components/shadcn/bubble.rb +14 -2
- data/app/components/shadcn/button_group.rb +5 -0
- data/app/components/shadcn/card.rb +11 -0
- data/app/components/shadcn/dropdown_menu.rb +21 -1
- data/app/components/shadcn/field.rb +11 -0
- data/app/components/shadcn/input_group/button.rb +26 -10
- data/app/components/shadcn/message.rb +10 -1
- data/app/components/shadcn/message_scroller/button.rb +49 -0
- data/app/components/shadcn/message_scroller.rb +0 -16
- data/app/components/shadcn/sidebar.rb +53 -7
- data/app/components/shadcn/toggle_group.rb +38 -7
- data/lib/shadcn_view_components/generated/contracts/sidebar.rb +2 -2
- data/lib/shadcn_view_components/property_contract_definitions.rb +30 -1
- data/lib/shadcn_view_components/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a817d07fcea9d79467f32d8ba3a0c2cd58fcafd2fac59664f50963bd099e551
|
|
4
|
+
data.tar.gz: 2c7d7f5b36b0c2ce319a3f159e98c0b685f02467b0c2737a5e2bcb6a39a1fe84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
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),
|
|
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
|
-
#
|
|
32
|
-
#
|
|
50
|
+
# upstream の <Button> が基本クラスと variant を付け、
|
|
51
|
+
# input-group 側の contract がサイズ調整を上書きする。
|
|
33
52
|
sig { override.returns(String) }
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
14
|
-
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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:
|
|
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:
|
|
112
|
-
size:
|
|
113
|
-
spacing:
|
|
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
|
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.
|
|
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
|