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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +590 -0
- data/LICENSE +49 -0
- data/README.md +542 -0
- data/app/assets/javascripts/shadcn/aria_relationships.js +88 -0
- data/app/assets/javascripts/shadcn/controllers/accordion_controller.js +241 -0
- data/app/assets/javascripts/shadcn/controllers/calendar_controller.js +98 -0
- data/app/assets/javascripts/shadcn/controllers/carousel_controller.js +305 -0
- data/app/assets/javascripts/shadcn/controllers/checked_state_controller.js +121 -0
- data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +613 -0
- data/app/assets/javascripts/shadcn/controllers/command_controller.js +87 -0
- data/app/assets/javascripts/shadcn/controllers/dialog_controller.js +208 -0
- data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +145 -0
- data/app/assets/javascripts/shadcn/controllers/input_otp_controller.js +416 -0
- data/app/assets/javascripts/shadcn/controllers/menu_controller.js +286 -0
- data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +430 -0
- data/app/assets/javascripts/shadcn/controllers/message_scroller_controller.js +37 -0
- data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +254 -0
- data/app/assets/javascripts/shadcn/controllers/popover_controller.js +117 -0
- data/app/assets/javascripts/shadcn/controllers/resizable_controller.js +256 -0
- data/app/assets/javascripts/shadcn/controllers/select_controller.js +491 -0
- data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +30 -0
- data/app/assets/javascripts/shadcn/controllers/slider_controller.js +74 -0
- data/app/assets/javascripts/shadcn/controllers/tabs_controller.js +146 -0
- data/app/assets/javascripts/shadcn/controllers/toast_controller.js +81 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_controller.js +21 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.js +37 -0
- data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +123 -0
- data/app/assets/javascripts/shadcn/floating_position.js +660 -0
- data/app/assets/javascripts/shadcn/hide_after_exit.js +53 -0
- data/app/assets/javascripts/shadcn/index.js +65 -0
- data/app/assets/javascripts/shadcn/menu_popover.js +352 -0
- data/app/assets/javascripts/shadcn/package.json +17 -0
- data/app/assets/javascripts/shadcn/state_attrs.js +13 -0
- data/app/assets/stylesheets/shadcn/shadcn.css +766 -0
- data/app/assets/tailwind/shadcn_view_components/engine.css +11 -0
- data/app/components/shadcn/accordion.rb +121 -0
- data/app/components/shadcn/alert.rb +31 -0
- data/app/components/shadcn/alert_dialog.rb +165 -0
- data/app/components/shadcn/aspect_ratio.rb +34 -0
- data/app/components/shadcn/attachment.rb +86 -0
- data/app/components/shadcn/avatar.rb +55 -0
- data/app/components/shadcn/badge.rb +32 -0
- data/app/components/shadcn/base_component.rb +239 -0
- data/app/components/shadcn/breadcrumb.rb +70 -0
- data/app/components/shadcn/bubble.rb +48 -0
- data/app/components/shadcn/button.html.erb +1 -0
- data/app/components/shadcn/button.rb +41 -0
- data/app/components/shadcn/button_group.rb +41 -0
- data/app/components/shadcn/button_styled.rb +51 -0
- data/app/components/shadcn/calendar.rb +276 -0
- data/app/components/shadcn/card.rb +18 -0
- data/app/components/shadcn/carousel.rb +236 -0
- data/app/components/shadcn/chart.rb +57 -0
- data/app/components/shadcn/checkbox.rb +64 -0
- data/app/components/shadcn/collapsible.rb +25 -0
- data/app/components/shadcn/combobox.rb +552 -0
- data/app/components/shadcn/command.rb +149 -0
- data/app/components/shadcn/context_menu.rb +79 -0
- data/app/components/shadcn/dialog.rb +180 -0
- data/app/components/shadcn/direction_provider.rb +18 -0
- data/app/components/shadcn/drawer.rb +150 -0
- data/app/components/shadcn/dropdown_menu.rb +242 -0
- data/app/components/shadcn/empty.rb +40 -0
- data/app/components/shadcn/field.rb +106 -0
- data/app/components/shadcn/floating_position_options.rb +132 -0
- data/app/components/shadcn/form.rb +105 -0
- data/app/components/shadcn/hover_card.rb +66 -0
- data/app/components/shadcn/input.rb +13 -0
- data/app/components/shadcn/input_group/button.rb +48 -0
- data/app/components/shadcn/input_group.rb +51 -0
- data/app/components/shadcn/input_otp.rb +265 -0
- data/app/components/shadcn/item.rb +108 -0
- data/app/components/shadcn/kbd.rb +9 -0
- data/app/components/shadcn/label.rb +12 -0
- data/app/components/shadcn/marker.rb +37 -0
- data/app/components/shadcn/menubar.rb +110 -0
- data/app/components/shadcn/message.rb +29 -0
- data/app/components/shadcn/message_scroller.rb +52 -0
- data/app/components/shadcn/native_checked_state.rb +74 -0
- data/app/components/shadcn/native_select.rb +78 -0
- data/app/components/shadcn/navigation_menu.rb +122 -0
- data/app/components/shadcn/pagination.rb +160 -0
- data/app/components/shadcn/popover.rb +68 -0
- data/app/components/shadcn/preview_base.rb +107 -0
- data/app/components/shadcn/progress.rb +49 -0
- data/app/components/shadcn/radio_group.rb +74 -0
- data/app/components/shadcn/resizable.rb +77 -0
- data/app/components/shadcn/scroll_area.rb +59 -0
- data/app/components/shadcn/select.rb +356 -0
- data/app/components/shadcn/separator.rb +42 -0
- data/app/components/shadcn/sheet.rb +157 -0
- data/app/components/shadcn/sidebar.rb +332 -0
- data/app/components/shadcn/skeleton.rb +13 -0
- data/app/components/shadcn/slider.rb +279 -0
- data/app/components/shadcn/sonner.rb +30 -0
- data/app/components/shadcn/spinner.rb +32 -0
- data/app/components/shadcn/switch.rb +79 -0
- data/app/components/shadcn/table.rb +46 -0
- data/app/components/shadcn/tabs.rb +119 -0
- data/app/components/shadcn/textarea.rb +7 -0
- data/app/components/shadcn/toggle.rb +66 -0
- data/app/components/shadcn/toggle_group.rb +127 -0
- data/app/components/shadcn/tooltip.rb +74 -0
- data/config/importmap.rb +5 -0
- data/lib/generators/shadcn_view_components/install_generator.rb +182 -0
- data/lib/shadcn_view_components/classes.rb +89 -0
- data/lib/shadcn_view_components/component_naming.rb +21 -0
- data/lib/shadcn_view_components/contracts/calendar.rb +95 -0
- data/lib/shadcn_view_components/engine.rb +56 -0
- data/lib/shadcn_view_components/generated/contracts/accordion.rb +112 -0
- data/lib/shadcn_view_components/generated/contracts/alert-dialog.rb +300 -0
- data/lib/shadcn_view_components/generated/contracts/alert.rb +104 -0
- data/lib/shadcn_view_components/generated/contracts/aspect-ratio.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/attachment.rb +224 -0
- data/lib/shadcn_view_components/generated/contracts/avatar.rb +149 -0
- data/lib/shadcn_view_components/generated/contracts/badge.rb +39 -0
- data/lib/shadcn_view_components/generated/contracts/breadcrumb.rb +172 -0
- data/lib/shadcn_view_components/generated/contracts/bubble.rb +112 -0
- data/lib/shadcn_view_components/generated/contracts/button-group.rb +81 -0
- data/lib/shadcn_view_components/generated/contracts/button.rb +81 -0
- data/lib/shadcn_view_components/generated/contracts/card.rb +172 -0
- data/lib/shadcn_view_components/generated/contracts/carousel.rb +133 -0
- data/lib/shadcn_view_components/generated/contracts/chart.rb +109 -0
- data/lib/shadcn_view_components/generated/contracts/checkbox.rb +35 -0
- data/lib/shadcn_view_components/generated/contracts/collapsible.rb +89 -0
- data/lib/shadcn_view_components/generated/contracts/combobox.rb +368 -0
- data/lib/shadcn_view_components/generated/contracts/command.rb +222 -0
- data/lib/shadcn_view_components/generated/contracts/context-menu.rb +378 -0
- data/lib/shadcn_view_components/generated/contracts/dialog.rb +258 -0
- data/lib/shadcn_view_components/generated/contracts/direction.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/drawer.rb +280 -0
- data/lib/shadcn_view_components/generated/contracts/dropdown-menu.rb +380 -0
- data/lib/shadcn_view_components/generated/contracts/empty.rb +150 -0
- data/lib/shadcn_view_components/generated/contracts/field.rb +244 -0
- data/lib/shadcn_view_components/generated/contracts/form.rb +62 -0
- data/lib/shadcn_view_components/generated/contracts/hover-card.rb +90 -0
- data/lib/shadcn_view_components/generated/contracts/input-group.rb +155 -0
- data/lib/shadcn_view_components/generated/contracts/input-otp.rb +106 -0
- data/lib/shadcn_view_components/generated/contracts/input.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/item.rb +251 -0
- data/lib/shadcn_view_components/generated/contracts/kbd.rb +57 -0
- data/lib/shadcn_view_components/generated/contracts/label.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/marker.rb +82 -0
- data/lib/shadcn_view_components/generated/contracts/menubar.rb +394 -0
- data/lib/shadcn_view_components/generated/contracts/message-scroller.rb +149 -0
- data/lib/shadcn_view_components/generated/contracts/message.rb +149 -0
- data/lib/shadcn_view_components/generated/contracts/native-select.rb +82 -0
- data/lib/shadcn_view_components/generated/contracts/navigation-menu.rb +195 -0
- data/lib/shadcn_view_components/generated/contracts/pagination.rb +176 -0
- data/lib/shadcn_view_components/generated/contracts/popover.rb +159 -0
- data/lib/shadcn_view_components/generated/contracts/progress.rb +126 -0
- data/lib/shadcn_view_components/generated/contracts/radio-group.rb +58 -0
- data/lib/shadcn_view_components/generated/contracts/resizable.rb +85 -0
- data/lib/shadcn_view_components/generated/contracts/scroll-area.rb +59 -0
- data/lib/shadcn_view_components/generated/contracts/select.rb +245 -0
- data/lib/shadcn_view_components/generated/contracts/separator.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/sheet.rb +209 -0
- data/lib/shadcn_view_components/generated/contracts/sidebar.rb +558 -0
- data/lib/shadcn_view_components/generated/contracts/skeleton.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/slider.rb +38 -0
- data/lib/shadcn_view_components/generated/contracts/sonner.rb +36 -0
- data/lib/shadcn_view_components/generated/contracts/spinner.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/switch.rb +35 -0
- data/lib/shadcn_view_components/generated/contracts/table.rb +199 -0
- data/lib/shadcn_view_components/generated/contracts/tabs.rb +104 -0
- data/lib/shadcn_view_components/generated/contracts/textarea.rb +34 -0
- data/lib/shadcn_view_components/generated/contracts/toggle-group.rb +62 -0
- data/lib/shadcn_view_components/generated/contracts/toggle.rb +39 -0
- data/lib/shadcn_view_components/generated/contracts/tooltip.rb +116 -0
- data/lib/shadcn_view_components/numeric_property_validation.rb +124 -0
- data/lib/shadcn_view_components/property_contract_definitions.rb +92 -0
- data/lib/shadcn_view_components/property_contracts.rb +42 -0
- data/lib/shadcn_view_components/property_validation.rb +55 -0
- data/lib/shadcn_view_components/version.rb +6 -0
- data/lib/shadcn_view_components.rb +21 -0
- 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,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"
|