shadcn_view_components 0.2.0

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.
Files changed (177) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +590 -0
  3. data/LICENSE +49 -0
  4. data/README.md +542 -0
  5. data/app/assets/javascripts/shadcn/aria_relationships.js +88 -0
  6. data/app/assets/javascripts/shadcn/controllers/accordion_controller.js +241 -0
  7. data/app/assets/javascripts/shadcn/controllers/calendar_controller.js +98 -0
  8. data/app/assets/javascripts/shadcn/controllers/carousel_controller.js +305 -0
  9. data/app/assets/javascripts/shadcn/controllers/checked_state_controller.js +121 -0
  10. data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +613 -0
  11. data/app/assets/javascripts/shadcn/controllers/command_controller.js +87 -0
  12. data/app/assets/javascripts/shadcn/controllers/dialog_controller.js +208 -0
  13. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +145 -0
  14. data/app/assets/javascripts/shadcn/controllers/input_otp_controller.js +416 -0
  15. data/app/assets/javascripts/shadcn/controllers/menu_controller.js +286 -0
  16. data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +430 -0
  17. data/app/assets/javascripts/shadcn/controllers/message_scroller_controller.js +37 -0
  18. data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +254 -0
  19. data/app/assets/javascripts/shadcn/controllers/popover_controller.js +117 -0
  20. data/app/assets/javascripts/shadcn/controllers/resizable_controller.js +256 -0
  21. data/app/assets/javascripts/shadcn/controllers/select_controller.js +491 -0
  22. data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +30 -0
  23. data/app/assets/javascripts/shadcn/controllers/slider_controller.js +74 -0
  24. data/app/assets/javascripts/shadcn/controllers/tabs_controller.js +146 -0
  25. data/app/assets/javascripts/shadcn/controllers/toast_controller.js +81 -0
  26. data/app/assets/javascripts/shadcn/controllers/toggle_controller.js +21 -0
  27. data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.js +37 -0
  28. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +123 -0
  29. data/app/assets/javascripts/shadcn/floating_position.js +660 -0
  30. data/app/assets/javascripts/shadcn/hide_after_exit.js +53 -0
  31. data/app/assets/javascripts/shadcn/index.js +65 -0
  32. data/app/assets/javascripts/shadcn/menu_popover.js +352 -0
  33. data/app/assets/javascripts/shadcn/package.json +17 -0
  34. data/app/assets/javascripts/shadcn/state_attrs.js +13 -0
  35. data/app/assets/stylesheets/shadcn/shadcn.css +766 -0
  36. data/app/assets/tailwind/shadcn_view_components/engine.css +11 -0
  37. data/app/components/shadcn/accordion.rb +121 -0
  38. data/app/components/shadcn/alert.rb +31 -0
  39. data/app/components/shadcn/alert_dialog.rb +165 -0
  40. data/app/components/shadcn/aspect_ratio.rb +34 -0
  41. data/app/components/shadcn/attachment.rb +86 -0
  42. data/app/components/shadcn/avatar.rb +55 -0
  43. data/app/components/shadcn/badge.rb +32 -0
  44. data/app/components/shadcn/base_component.rb +239 -0
  45. data/app/components/shadcn/breadcrumb.rb +70 -0
  46. data/app/components/shadcn/bubble.rb +48 -0
  47. data/app/components/shadcn/button.html.erb +1 -0
  48. data/app/components/shadcn/button.rb +41 -0
  49. data/app/components/shadcn/button_group.rb +41 -0
  50. data/app/components/shadcn/button_styled.rb +51 -0
  51. data/app/components/shadcn/calendar.rb +276 -0
  52. data/app/components/shadcn/card.rb +18 -0
  53. data/app/components/shadcn/carousel.rb +236 -0
  54. data/app/components/shadcn/chart.rb +57 -0
  55. data/app/components/shadcn/checkbox.rb +64 -0
  56. data/app/components/shadcn/collapsible.rb +25 -0
  57. data/app/components/shadcn/combobox.rb +552 -0
  58. data/app/components/shadcn/command.rb +149 -0
  59. data/app/components/shadcn/context_menu.rb +79 -0
  60. data/app/components/shadcn/dialog.rb +180 -0
  61. data/app/components/shadcn/direction_provider.rb +18 -0
  62. data/app/components/shadcn/drawer.rb +150 -0
  63. data/app/components/shadcn/dropdown_menu.rb +242 -0
  64. data/app/components/shadcn/empty.rb +40 -0
  65. data/app/components/shadcn/field.rb +106 -0
  66. data/app/components/shadcn/floating_position_options.rb +132 -0
  67. data/app/components/shadcn/form.rb +105 -0
  68. data/app/components/shadcn/hover_card.rb +66 -0
  69. data/app/components/shadcn/input.rb +13 -0
  70. data/app/components/shadcn/input_group/button.rb +48 -0
  71. data/app/components/shadcn/input_group.rb +51 -0
  72. data/app/components/shadcn/input_otp.rb +265 -0
  73. data/app/components/shadcn/item.rb +108 -0
  74. data/app/components/shadcn/kbd.rb +9 -0
  75. data/app/components/shadcn/label.rb +12 -0
  76. data/app/components/shadcn/marker.rb +37 -0
  77. data/app/components/shadcn/menubar.rb +110 -0
  78. data/app/components/shadcn/message.rb +29 -0
  79. data/app/components/shadcn/message_scroller.rb +52 -0
  80. data/app/components/shadcn/native_checked_state.rb +74 -0
  81. data/app/components/shadcn/native_select.rb +78 -0
  82. data/app/components/shadcn/navigation_menu.rb +122 -0
  83. data/app/components/shadcn/pagination.rb +160 -0
  84. data/app/components/shadcn/popover.rb +68 -0
  85. data/app/components/shadcn/preview_base.rb +107 -0
  86. data/app/components/shadcn/progress.rb +49 -0
  87. data/app/components/shadcn/radio_group.rb +74 -0
  88. data/app/components/shadcn/resizable.rb +77 -0
  89. data/app/components/shadcn/scroll_area.rb +59 -0
  90. data/app/components/shadcn/select.rb +356 -0
  91. data/app/components/shadcn/separator.rb +42 -0
  92. data/app/components/shadcn/sheet.rb +157 -0
  93. data/app/components/shadcn/sidebar.rb +332 -0
  94. data/app/components/shadcn/skeleton.rb +13 -0
  95. data/app/components/shadcn/slider.rb +279 -0
  96. data/app/components/shadcn/sonner.rb +30 -0
  97. data/app/components/shadcn/spinner.rb +32 -0
  98. data/app/components/shadcn/switch.rb +79 -0
  99. data/app/components/shadcn/table.rb +46 -0
  100. data/app/components/shadcn/tabs.rb +119 -0
  101. data/app/components/shadcn/textarea.rb +7 -0
  102. data/app/components/shadcn/toggle.rb +66 -0
  103. data/app/components/shadcn/toggle_group.rb +127 -0
  104. data/app/components/shadcn/tooltip.rb +74 -0
  105. data/config/importmap.rb +5 -0
  106. data/lib/generators/shadcn_view_components/install_generator.rb +182 -0
  107. data/lib/shadcn_view_components/classes.rb +89 -0
  108. data/lib/shadcn_view_components/component_naming.rb +21 -0
  109. data/lib/shadcn_view_components/contracts/calendar.rb +95 -0
  110. data/lib/shadcn_view_components/engine.rb +56 -0
  111. data/lib/shadcn_view_components/generated/contracts/accordion.rb +112 -0
  112. data/lib/shadcn_view_components/generated/contracts/alert-dialog.rb +300 -0
  113. data/lib/shadcn_view_components/generated/contracts/alert.rb +104 -0
  114. data/lib/shadcn_view_components/generated/contracts/aspect-ratio.rb +34 -0
  115. data/lib/shadcn_view_components/generated/contracts/attachment.rb +224 -0
  116. data/lib/shadcn_view_components/generated/contracts/avatar.rb +149 -0
  117. data/lib/shadcn_view_components/generated/contracts/badge.rb +39 -0
  118. data/lib/shadcn_view_components/generated/contracts/breadcrumb.rb +172 -0
  119. data/lib/shadcn_view_components/generated/contracts/bubble.rb +112 -0
  120. data/lib/shadcn_view_components/generated/contracts/button-group.rb +81 -0
  121. data/lib/shadcn_view_components/generated/contracts/button.rb +81 -0
  122. data/lib/shadcn_view_components/generated/contracts/card.rb +172 -0
  123. data/lib/shadcn_view_components/generated/contracts/carousel.rb +133 -0
  124. data/lib/shadcn_view_components/generated/contracts/chart.rb +109 -0
  125. data/lib/shadcn_view_components/generated/contracts/checkbox.rb +35 -0
  126. data/lib/shadcn_view_components/generated/contracts/collapsible.rb +89 -0
  127. data/lib/shadcn_view_components/generated/contracts/combobox.rb +368 -0
  128. data/lib/shadcn_view_components/generated/contracts/command.rb +222 -0
  129. data/lib/shadcn_view_components/generated/contracts/context-menu.rb +378 -0
  130. data/lib/shadcn_view_components/generated/contracts/dialog.rb +258 -0
  131. data/lib/shadcn_view_components/generated/contracts/direction.rb +34 -0
  132. data/lib/shadcn_view_components/generated/contracts/drawer.rb +280 -0
  133. data/lib/shadcn_view_components/generated/contracts/dropdown-menu.rb +380 -0
  134. data/lib/shadcn_view_components/generated/contracts/empty.rb +150 -0
  135. data/lib/shadcn_view_components/generated/contracts/field.rb +244 -0
  136. data/lib/shadcn_view_components/generated/contracts/form.rb +62 -0
  137. data/lib/shadcn_view_components/generated/contracts/hover-card.rb +90 -0
  138. data/lib/shadcn_view_components/generated/contracts/input-group.rb +155 -0
  139. data/lib/shadcn_view_components/generated/contracts/input-otp.rb +106 -0
  140. data/lib/shadcn_view_components/generated/contracts/input.rb +34 -0
  141. data/lib/shadcn_view_components/generated/contracts/item.rb +251 -0
  142. data/lib/shadcn_view_components/generated/contracts/kbd.rb +57 -0
  143. data/lib/shadcn_view_components/generated/contracts/label.rb +34 -0
  144. data/lib/shadcn_view_components/generated/contracts/marker.rb +82 -0
  145. data/lib/shadcn_view_components/generated/contracts/menubar.rb +394 -0
  146. data/lib/shadcn_view_components/generated/contracts/message-scroller.rb +149 -0
  147. data/lib/shadcn_view_components/generated/contracts/message.rb +149 -0
  148. data/lib/shadcn_view_components/generated/contracts/native-select.rb +82 -0
  149. data/lib/shadcn_view_components/generated/contracts/navigation-menu.rb +195 -0
  150. data/lib/shadcn_view_components/generated/contracts/pagination.rb +176 -0
  151. data/lib/shadcn_view_components/generated/contracts/popover.rb +159 -0
  152. data/lib/shadcn_view_components/generated/contracts/progress.rb +126 -0
  153. data/lib/shadcn_view_components/generated/contracts/radio-group.rb +58 -0
  154. data/lib/shadcn_view_components/generated/contracts/resizable.rb +85 -0
  155. data/lib/shadcn_view_components/generated/contracts/scroll-area.rb +59 -0
  156. data/lib/shadcn_view_components/generated/contracts/select.rb +245 -0
  157. data/lib/shadcn_view_components/generated/contracts/separator.rb +34 -0
  158. data/lib/shadcn_view_components/generated/contracts/sheet.rb +209 -0
  159. data/lib/shadcn_view_components/generated/contracts/sidebar.rb +558 -0
  160. data/lib/shadcn_view_components/generated/contracts/skeleton.rb +34 -0
  161. data/lib/shadcn_view_components/generated/contracts/slider.rb +38 -0
  162. data/lib/shadcn_view_components/generated/contracts/sonner.rb +36 -0
  163. data/lib/shadcn_view_components/generated/contracts/spinner.rb +34 -0
  164. data/lib/shadcn_view_components/generated/contracts/switch.rb +35 -0
  165. data/lib/shadcn_view_components/generated/contracts/table.rb +199 -0
  166. data/lib/shadcn_view_components/generated/contracts/tabs.rb +104 -0
  167. data/lib/shadcn_view_components/generated/contracts/textarea.rb +34 -0
  168. data/lib/shadcn_view_components/generated/contracts/toggle-group.rb +62 -0
  169. data/lib/shadcn_view_components/generated/contracts/toggle.rb +39 -0
  170. data/lib/shadcn_view_components/generated/contracts/tooltip.rb +116 -0
  171. data/lib/shadcn_view_components/numeric_property_validation.rb +124 -0
  172. data/lib/shadcn_view_components/property_contract_definitions.rb +92 -0
  173. data/lib/shadcn_view_components/property_contracts.rb +42 -0
  174. data/lib/shadcn_view_components/property_validation.rb +55 -0
  175. data/lib/shadcn_view_components/version.rb +6 -0
  176. data/lib/shadcn_view_components.rb +21 -0
  177. metadata +446 -0
@@ -0,0 +1,124 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module ShadcnViewComponents
5
+ module NumericPropertyValidation
6
+ extend T::Sig
7
+
8
+ NUMBER_PATTERN = /\A[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?\z/
9
+ INTEGER_PATTERN = /\A[+-]?\d+\z/
10
+
11
+ module_function
12
+
13
+ sig do
14
+ params(
15
+ owner: String,
16
+ property: Symbol,
17
+ value: T.untyped,
18
+ definition: T::Hash[Symbol, T.untyped]
19
+ ).returns(T.nilable(T.any(Integer, Float)))
20
+ end
21
+ def normalize(owner:, property:, value:, definition:)
22
+ return nil if value.nil? && definition.fetch(:allow_nil, false)
23
+
24
+ number = finite_number(value, integer: definition.fetch(:integer, false))
25
+ invalid_property!(owner, property, value, definition) unless number
26
+ validate_bounds!(owner, property, value, number, definition)
27
+ end
28
+
29
+ sig do
30
+ params(value: T.untyped, integer: T::Boolean)
31
+ .returns(T.nilable(T.any(Integer, Float)))
32
+ end
33
+ def finite_number(value, integer:)
34
+ number = integer ? parse_integer(value) : parse_number(value)
35
+ return number if number.is_a?(Integer)
36
+
37
+ number if number&.finite?
38
+ end
39
+
40
+ sig { params(value: T.untyped).returns(T.nilable(Integer)) }
41
+ def parse_integer(value)
42
+ return value if value.is_a?(Integer)
43
+ return unless value.is_a?(String)
44
+
45
+ normalized = value.strip
46
+ normalized.to_i if INTEGER_PATTERN.match?(normalized)
47
+ end
48
+
49
+ sig { params(value: T.untyped).returns(T.nilable(T.any(Integer, Float))) }
50
+ def parse_number(value)
51
+ return value if value.is_a?(Integer) || value.is_a?(Float)
52
+ return unless value.is_a?(String)
53
+
54
+ normalized = value.strip
55
+ return normalized.to_i if INTEGER_PATTERN.match?(normalized)
56
+
57
+ normalized.to_f if NUMBER_PATTERN.match?(normalized)
58
+ end
59
+
60
+ sig do
61
+ params(
62
+ owner: String,
63
+ property: Symbol,
64
+ value: T.untyped,
65
+ number: T.any(Integer, Float),
66
+ definition: T::Hash[Symbol, T.untyped]
67
+ ).returns(T.any(Integer, Float))
68
+ end
69
+ def validate_bounds!(owner, property, value, number, definition)
70
+ minimum = T.cast(definition[:minimum], T.nilable(T.any(Integer, Float)))
71
+ maximum = T.cast(definition[:maximum], T.nilable(T.any(Integer, Float)))
72
+ too_small = below_minimum?(number, minimum, definition.fetch(:exclusive_minimum, false))
73
+ too_large = maximum && number > maximum
74
+ invalid_property!(owner, property, value, definition) if too_small || too_large
75
+ number
76
+ end
77
+
78
+ sig do
79
+ params(
80
+ number: T.any(Integer, Float),
81
+ minimum: T.nilable(T.any(Integer, Float)),
82
+ exclusive: T::Boolean
83
+ ).returns(T::Boolean)
84
+ end
85
+ def below_minimum?(number, minimum, exclusive)
86
+ return false unless minimum
87
+
88
+ exclusive ? number <= minimum : number < minimum
89
+ end
90
+
91
+ sig { params(definition: T::Hash[Symbol, T.untyped]).returns(String) }
92
+ def number_requirement(definition)
93
+ description = definition.fetch(:integer, false) ? "a finite integer" : "a finite number"
94
+ description += bounds_requirement(definition)
95
+ description += " or nil" if definition.fetch(:allow_nil, false)
96
+ description
97
+ end
98
+
99
+ sig { params(definition: T::Hash[Symbol, T.untyped]).returns(String) }
100
+ def bounds_requirement(definition)
101
+ minimum = definition[:minimum]
102
+ maximum = definition[:maximum]
103
+ return " between #{minimum} and #{maximum}" if minimum && maximum
104
+ return " less than or equal to #{maximum}" if maximum
105
+ return "" unless minimum
106
+
107
+ comparison = definition.fetch(:exclusive_minimum, false) ? "greater than" : "greater than or equal to"
108
+ " #{comparison} #{minimum}"
109
+ end
110
+
111
+ sig do
112
+ params(
113
+ owner: String,
114
+ property: Symbol,
115
+ value: T.untyped,
116
+ definition: T::Hash[Symbol, T.untyped]
117
+ ).returns(T.noreturn)
118
+ end
119
+ def invalid_property!(owner, property, value, definition)
120
+ requirement = number_requirement(definition)
121
+ Kernel.raise ArgumentError, "#{owner} #{property} must be #{requirement}, got #{value.inspect}"
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,92 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module ShadcnViewComponents
5
+ module PropertyContracts
6
+ DIRECTIONS = %w[ltr rtl].freeze
7
+ ORIENTATIONS = %w[horizontal vertical].freeze
8
+ TOGGLE_STATES = %w[off on].freeze
9
+
10
+ DEFINITIONS = T.let({
11
+ progress: {
12
+ value: { kind: :number, default: nil, allow_nil: true, minimum: 0, maximum: 100 }.freeze
13
+ }.freeze,
14
+ slider: {
15
+ min: { kind: :number, default: 0 }.freeze,
16
+ max: { kind: :number, default: 100 }.freeze,
17
+ step: { kind: :number, default: 1, minimum: 0, exclusive_minimum: true }.freeze,
18
+ value: { kind: :number, default: nil, allow_nil: true }.freeze,
19
+ orientation: { kind: :enum, default: "horizontal", values: ORIENTATIONS }.freeze
20
+ }.freeze,
21
+ "sheet/content": {
22
+ side: { kind: :enum, default: "right", values: %w[top right bottom left].freeze }.freeze
23
+ }.freeze,
24
+ "scroll_area/scrollbar": {
25
+ orientation: { kind: :enum, default: "vertical", values: ORIENTATIONS }.freeze
26
+ }.freeze,
27
+ tabs: {
28
+ orientation: { kind: :enum, default: "horizontal", values: ORIENTATIONS }.freeze
29
+ }.freeze,
30
+ carousel: {
31
+ orientation: { kind: :enum, default: "horizontal", values: ORIENTATIONS }.freeze,
32
+ direction: { kind: :enum, default: "ltr", values: DIRECTIONS }.freeze
33
+ }.freeze,
34
+ "resizable/panel_group": {
35
+ orientation: { kind: :enum, default: "horizontal", values: ORIENTATIONS }.freeze
36
+ }.freeze,
37
+ "resizable/handle": {
38
+ orientation: { kind: :enum, default: "vertical", values: ORIENTATIONS }.freeze
39
+ }.freeze,
40
+ separator: {
41
+ orientation: { kind: :enum, default: "horizontal", values: ORIENTATIONS }.freeze
42
+ }.freeze,
43
+ toggle: {
44
+ state: { kind: :enum, default: "off", values: TOGGLE_STATES }.freeze
45
+ }.freeze,
46
+ toggle_group: {
47
+ type: { kind: :enum, default: "multiple", values: %w[single multiple].freeze }.freeze,
48
+ spacing: { kind: :number, default: 2, minimum: 0 }.freeze
49
+ }.freeze,
50
+ "toggle_group/item": {
51
+ state: { kind: :enum, default: "off", values: TOGGLE_STATES }.freeze,
52
+ spacing: { kind: :number, default: 2, minimum: 0 }.freeze
53
+ }.freeze,
54
+ direction_provider: {
55
+ dir: { kind: :enum, default: "ltr", values: DIRECTIONS }.freeze
56
+ }.freeze,
57
+ sidebar: {
58
+ state: { kind: :enum, default: "open", values: %w[open closed].freeze }.freeze
59
+ }.freeze,
60
+ "sidebar/provider": {
61
+ state: { kind: :enum, default: "open", values: %w[open closed].freeze }.freeze
62
+ }.freeze,
63
+ "sidebar/menu_sub_button": {
64
+ size: { kind: :enum, default: "md", values: %w[sm md].freeze }.freeze
65
+ }.freeze,
66
+ "alert_dialog/content": {
67
+ size: { kind: :enum, default: "default", values: %w[default sm].freeze }.freeze
68
+ }.freeze,
69
+ avatar: {
70
+ size: { kind: :enum, default: "default", values: %w[default sm lg].freeze }.freeze
71
+ }.freeze,
72
+ switch: {
73
+ size: { kind: :enum, default: "default", values: %w[default sm].freeze }.freeze
74
+ }.freeze,
75
+ "select/trigger": {
76
+ size: { kind: :enum, default: "default", values: %w[default sm].freeze }.freeze
77
+ }.freeze,
78
+ native_select: {
79
+ size: { kind: :enum, default: "default", values: %w[default sm].freeze }.freeze
80
+ }.freeze,
81
+ aspect_ratio: {
82
+ ratio: { kind: :number, default: nil, allow_nil: true, minimum: 0, exclusive_minimum: true }.freeze
83
+ }.freeze,
84
+ input_otp: {
85
+ length: { kind: :number, default: 6, integer: true, minimum: 1 }.freeze
86
+ }.freeze,
87
+ "input_otp/slot": {
88
+ index: { kind: :number, default: 0, integer: true, minimum: 0 }.freeze
89
+ }.freeze
90
+ }.freeze, T::Hash[Symbol, T::Hash[Symbol, T::Hash[Symbol, T.untyped]]])
91
+ end
92
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "property_contract_definitions"
5
+ require_relative "property_validation"
6
+
7
+ module ShadcnViewComponents
8
+ # CVA由来のVARIANTSでは表現できない、Rails公開API固有の意味的なprop契約。
9
+ # data属性・ARIA・CSS値へ出す前に、この定義を唯一の値域として正規化する。
10
+ module PropertyContracts
11
+ extend T::Sig
12
+
13
+ module_function
14
+
15
+ sig { params(component: Symbol, property: Symbol).returns(T::Hash[Symbol, T.untyped]) }
16
+ def fetch(component, property)
17
+ properties = DEFINITIONS.fetch(component) do
18
+ Kernel.raise KeyError, "no property contract for #{component.inspect}"
19
+ end
20
+ properties.fetch(property) do
21
+ Kernel.raise KeyError, "no property contract for #{component.inspect}.#{property}"
22
+ end
23
+ end
24
+
25
+ sig { params(component: Symbol, property: Symbol).returns(T.untyped) }
26
+ def default(component, property)
27
+ fetch(component, property).fetch(:default)
28
+ end
29
+
30
+ sig do
31
+ params(
32
+ component: Symbol,
33
+ owner: String,
34
+ property: Symbol,
35
+ value: T.untyped
36
+ ).returns(T.untyped)
37
+ end
38
+ def normalize(component:, owner:, property:, value:)
39
+ PropertyValidation.normalize(owner:, property:, value:, definition: fetch(component, property))
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "numeric_property_validation"
5
+
6
+ module ShadcnViewComponents
7
+ # PropertyContractsに宣言された列挙値・数値を、描画前に正規化する。
8
+ module PropertyValidation
9
+ extend T::Sig
10
+
11
+ module_function
12
+
13
+ sig do
14
+ params(
15
+ owner: String,
16
+ property: Symbol,
17
+ value: T.untyped,
18
+ definition: T::Hash[Symbol, T.untyped]
19
+ ).returns(T.untyped)
20
+ end
21
+ def normalize(owner:, property:, value:, definition:)
22
+ case definition.fetch(:kind)
23
+ when :enum
24
+ normalize_enum(owner, property, value, definition)
25
+ when :number
26
+ NumericPropertyValidation.normalize(owner:, property:, value:, definition:)
27
+ else
28
+ Kernel.raise KeyError, "unknown property contract kind for #{owner}.#{property}"
29
+ end
30
+ end
31
+
32
+ sig do
33
+ params(
34
+ owner: String,
35
+ property: Symbol,
36
+ value: T.untyped,
37
+ definition: T::Hash[Symbol, T.untyped]
38
+ ).returns(String)
39
+ end
40
+ def normalize_enum(owner, property, value, definition)
41
+ values = T.cast(definition.fetch(:values), T::Array[String])
42
+ invalid_property!(owner, property, value, "one of #{values.map(&:inspect).join(', ')}") unless value.is_a?(String) || value.is_a?(Symbol)
43
+
44
+ normalized = value.to_s
45
+ return normalized if values.include?(normalized)
46
+
47
+ invalid_property!(owner, property, value, "one of #{values.map(&:inspect).join(', ')}")
48
+ end
49
+
50
+ sig { params(owner: String, property: Symbol, value: T.untyped, requirement: String).returns(T.noreturn) }
51
+ def invalid_property!(owner, property, value, requirement)
52
+ Kernel.raise ArgumentError, "#{owner} #{property} must be #{requirement}, got #{value.inspect}"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module ShadcnViewComponents
5
+ VERSION = "0.2.0"
6
+ end
@@ -0,0 +1,21 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # 生成物(contracts/*.rb)の T.let / sig がロード時に評価されるため、
5
+ # view_component と sorbet-runtime を最初に明示ロードする
6
+ require "sorbet-runtime"
7
+ require "tailwindcss-rails"
8
+ require "view_component"
9
+
10
+ require_relative "shadcn_view_components/version"
11
+ require_relative "shadcn_view_components/component_naming"
12
+ require_relative "shadcn_view_components/property_contracts"
13
+ require_relative "shadcn_view_components/engine"
14
+ require_relative "shadcn_view_components/classes"
15
+
16
+ # 生成された契約モジュール(lib/shadcn_view_components/generated/contracts/*.rb)。
17
+ # app/components の実装はこの定数のみを参照する(クラス文字列を手書きしない規約)。
18
+ Dir[File.expand_path("shadcn_view_components/generated/contracts/*.rb", __dir__)].each { |path| require path }
19
+
20
+ # 個別契約(静的抽出の対象外コンポーネント — calendar)
21
+ require_relative "shadcn_view_components/contracts/calendar"