shadcn-phlex 0.1.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/README.md +195 -0
- data/app.css +20 -0
- data/css/shadcn-source.css +3 -0
- data/css/shadcn-tailwind.css +160 -0
- data/css/themes/mauve.css +62 -0
- data/css/themes/mist.css +62 -0
- data/css/themes/neutral.css +74 -0
- data/css/themes/olive.css +62 -0
- data/css/themes/stone.css +62 -0
- data/css/themes/taupe.css +62 -0
- data/css/themes/zinc.css +62 -0
- data/js/controllers/accordion_controller.js +135 -0
- data/js/controllers/checkbox_controller.js +52 -0
- data/js/controllers/collapsible_controller.js +85 -0
- data/js/controllers/combobox_controller.js +168 -0
- data/js/controllers/command_controller.js +171 -0
- data/js/controllers/context_menu_controller.js +132 -0
- data/js/controllers/dark_mode_controller.js +106 -0
- data/js/controllers/dialog_controller.js +205 -0
- data/js/controllers/drawer_controller.js +161 -0
- data/js/controllers/dropdown_menu_controller.js +189 -0
- data/js/controllers/hover_card_controller.js +85 -0
- data/js/controllers/index.js +89 -0
- data/js/controllers/menubar_controller.js +171 -0
- data/js/controllers/navigation_menu_controller.js +160 -0
- data/js/controllers/popover_controller.js +151 -0
- data/js/controllers/radio_group_controller.js +78 -0
- data/js/controllers/scroll_area_controller.js +117 -0
- data/js/controllers/select_controller.js +198 -0
- data/js/controllers/sheet_controller.js +130 -0
- data/js/controllers/slider_controller.js +142 -0
- data/js/controllers/switch_controller.js +40 -0
- data/js/controllers/tabs_controller.js +96 -0
- data/js/controllers/toast_controller.js +206 -0
- data/js/controllers/toggle_controller.js +30 -0
- data/js/controllers/toggle_group_controller.js +73 -0
- data/js/controllers/tooltip_controller.js +146 -0
- data/lib/generators/shadcn_phlex/component_generator.rb +79 -0
- data/lib/generators/shadcn_phlex/install_generator.rb +217 -0
- data/lib/shadcn/base.rb +27 -0
- data/lib/shadcn/engine.rb +24 -0
- data/lib/shadcn/kit.rb +1158 -0
- data/lib/shadcn/themes/accent_colors.rb +106 -0
- data/lib/shadcn/themes/base_colors.rb +313 -0
- data/lib/shadcn/ui/accordion.rb +135 -0
- data/lib/shadcn/ui/alert.rb +79 -0
- data/lib/shadcn/ui/alert_dialog.rb +220 -0
- data/lib/shadcn/ui/aspect_ratio.rb +35 -0
- data/lib/shadcn/ui/avatar.rb +134 -0
- data/lib/shadcn/ui/badge.rb +48 -0
- data/lib/shadcn/ui/breadcrumb.rb +180 -0
- data/lib/shadcn/ui/button.rb +63 -0
- data/lib/shadcn/ui/button_group.rb +58 -0
- data/lib/shadcn/ui/card.rb +133 -0
- data/lib/shadcn/ui/checkbox.rb +72 -0
- data/lib/shadcn/ui/collapsible.rb +76 -0
- data/lib/shadcn/ui/combobox.rb +229 -0
- data/lib/shadcn/ui/command.rb +256 -0
- data/lib/shadcn/ui/context_menu.rb +319 -0
- data/lib/shadcn/ui/dialog.rb +226 -0
- data/lib/shadcn/ui/direction.rb +23 -0
- data/lib/shadcn/ui/drawer.rb +217 -0
- data/lib/shadcn/ui/dropdown_menu.rb +384 -0
- data/lib/shadcn/ui/empty.rb +97 -0
- data/lib/shadcn/ui/field.rb +126 -0
- data/lib/shadcn/ui/hover_card.rb +75 -0
- data/lib/shadcn/ui/input.rb +36 -0
- data/lib/shadcn/ui/input_group.rb +32 -0
- data/lib/shadcn/ui/input_otp.rb +112 -0
- data/lib/shadcn/ui/item.rb +115 -0
- data/lib/shadcn/ui/kbd.rb +45 -0
- data/lib/shadcn/ui/label.rb +28 -0
- data/lib/shadcn/ui/menubar.rb +345 -0
- data/lib/shadcn/ui/native_select.rb +31 -0
- data/lib/shadcn/ui/navigation_menu.rb +238 -0
- data/lib/shadcn/ui/pagination.rb +224 -0
- data/lib/shadcn/ui/popover.rb +147 -0
- data/lib/shadcn/ui/progress.rb +40 -0
- data/lib/shadcn/ui/radio_group.rb +92 -0
- data/lib/shadcn/ui/resizable.rb +108 -0
- data/lib/shadcn/ui/scroll_area.rb +75 -0
- data/lib/shadcn/ui/select.rb +235 -0
- data/lib/shadcn/ui/separator.rb +36 -0
- data/lib/shadcn/ui/sheet.rb +231 -0
- data/lib/shadcn/ui/sidebar.rb +420 -0
- data/lib/shadcn/ui/skeleton.rb +23 -0
- data/lib/shadcn/ui/slider.rb +72 -0
- data/lib/shadcn/ui/sonner.rb +177 -0
- data/lib/shadcn/ui/spinner.rb +58 -0
- data/lib/shadcn/ui/switch.rb +75 -0
- data/lib/shadcn/ui/table.rb +154 -0
- data/lib/shadcn/ui/tabs.rb +154 -0
- data/lib/shadcn/ui/text_field.rb +146 -0
- data/lib/shadcn/ui/textarea.rb +32 -0
- data/lib/shadcn/ui/theme_toggle.rb +74 -0
- data/lib/shadcn/ui/toggle.rb +66 -0
- data/lib/shadcn/ui/toggle_group.rb +75 -0
- data/lib/shadcn/ui/tooltip.rb +78 -0
- data/lib/shadcn/ui/typography.rb +217 -0
- data/lib/shadcn/version.rb +5 -0
- data/lib/shadcn-phlex.rb +6 -0
- data/lib/shadcn.rb +80 -0
- data/package.json +14 -0
- data/skills/shadcn-phlex/SKILL.md +190 -0
- data/skills/shadcn-phlex/evals/evals.json +90 -0
- data/skills/shadcn-phlex/references/component-catalog.md +355 -0
- data/skills/shadcn-phlex/rules/composition.md +235 -0
- data/skills/shadcn-phlex/rules/forms.md +151 -0
- data/skills/shadcn-phlex/rules/helpers.md +54 -0
- data/skills/shadcn-phlex/rules/stimulus.md +61 -0
- data/skills/shadcn-phlex/rules/styling.md +177 -0
- metadata +209 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Compound field component: Field + Label + Input + FieldDescription + FieldError
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# render Shadcn::UI::TextField.new(
|
|
9
|
+
# label: "Email",
|
|
10
|
+
# name: "user[email]",
|
|
11
|
+
# type: "email",
|
|
12
|
+
# description: "We'll never share your email.",
|
|
13
|
+
# error: "is required",
|
|
14
|
+
# required: true,
|
|
15
|
+
# placeholder: "you@example.com"
|
|
16
|
+
# )
|
|
17
|
+
class TextField < Base
|
|
18
|
+
def initialize(label: nil, name: nil, type: "text", description: nil, error: nil, required: false, disabled: false, id: nil, **input_attrs)
|
|
19
|
+
@label = label
|
|
20
|
+
@name = name
|
|
21
|
+
@type = type
|
|
22
|
+
@description = description
|
|
23
|
+
@error = error
|
|
24
|
+
@required = required
|
|
25
|
+
@disabled = disabled
|
|
26
|
+
@id = id || generate_id(name)
|
|
27
|
+
@input_attrs = input_attrs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def view_template
|
|
31
|
+
render Field.new(disabled: @disabled) do
|
|
32
|
+
if @label
|
|
33
|
+
render Label.new(for: @id) do
|
|
34
|
+
plain @label
|
|
35
|
+
if @required
|
|
36
|
+
span(class: "text-destructive") { " *" }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
render Input.new(
|
|
42
|
+
type: @type,
|
|
43
|
+
name: @name,
|
|
44
|
+
id: @id,
|
|
45
|
+
required: @required || nil,
|
|
46
|
+
disabled: @disabled || nil,
|
|
47
|
+
aria_invalid: @error ? "true" : nil,
|
|
48
|
+
aria_describedby: description_id,
|
|
49
|
+
**@input_attrs
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if @description && !@error
|
|
53
|
+
render FieldDescription.new(id: description_id) {
|
|
54
|
+
plain @description
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if @error
|
|
59
|
+
render FieldError.new { plain @error }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def generate_id(name)
|
|
67
|
+
return nil unless name
|
|
68
|
+
name.to_s.gsub(/[\[\]]/, "_").gsub(/_+/, "_").chomp("_")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def description_id
|
|
72
|
+
return nil unless @description && @id
|
|
73
|
+
"#{@id}_description"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Compound field component: Field + Label + Textarea + FieldDescription + FieldError
|
|
78
|
+
#
|
|
79
|
+
# Usage:
|
|
80
|
+
# render Shadcn::UI::TextareaField.new(
|
|
81
|
+
# label: "Message",
|
|
82
|
+
# name: "contact[message]",
|
|
83
|
+
# description: "Max 500 characters.",
|
|
84
|
+
# error: "can't be blank",
|
|
85
|
+
# required: true,
|
|
86
|
+
# rows: 4
|
|
87
|
+
# )
|
|
88
|
+
class TextareaField < Base
|
|
89
|
+
def initialize(label: nil, name: nil, description: nil, error: nil, required: false, disabled: false, id: nil, **textarea_attrs)
|
|
90
|
+
@label = label
|
|
91
|
+
@name = name
|
|
92
|
+
@description = description
|
|
93
|
+
@error = error
|
|
94
|
+
@required = required
|
|
95
|
+
@disabled = disabled
|
|
96
|
+
@id = id || generate_id(name)
|
|
97
|
+
@textarea_attrs = textarea_attrs
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def view_template
|
|
101
|
+
render Field.new(disabled: @disabled) do
|
|
102
|
+
if @label
|
|
103
|
+
render Label.new(for: @id) do
|
|
104
|
+
plain @label
|
|
105
|
+
if @required
|
|
106
|
+
span(class: "text-destructive") { " *" }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
render Textarea.new(
|
|
112
|
+
name: @name,
|
|
113
|
+
id: @id,
|
|
114
|
+
required: @required || nil,
|
|
115
|
+
disabled: @disabled || nil,
|
|
116
|
+
aria_invalid: @error ? "true" : nil,
|
|
117
|
+
aria_describedby: description_id,
|
|
118
|
+
**@textarea_attrs
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if @description && !@error
|
|
122
|
+
render FieldDescription.new(id: description_id) {
|
|
123
|
+
plain @description
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if @error
|
|
128
|
+
render FieldError.new { plain @error }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def generate_id(name)
|
|
136
|
+
return nil unless name
|
|
137
|
+
name.to_s.gsub(/[\[\]]/, "_").gsub(/_+/, "_").chomp("_")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def description_id
|
|
141
|
+
return nil unless @description && @id
|
|
142
|
+
"#{@id}_description"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Textarea
|
|
6
|
+
class Textarea < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
textarea(**build_attrs, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_attrs
|
|
18
|
+
classes = cn(
|
|
19
|
+
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2",
|
|
20
|
+
"text-base shadow-xs transition-[color,box-shadow] outline-none",
|
|
21
|
+
"placeholder:text-muted-foreground",
|
|
22
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
25
|
+
"md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
26
|
+
@attrs.delete(:class)
|
|
27
|
+
)
|
|
28
|
+
@attrs.merge(data_slot: "textarea", class: classes)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Dark mode toggle button
|
|
6
|
+
# Wired to shadcn--dark-mode Stimulus controller
|
|
7
|
+
# Renders a button with sun/moon SVG icons that swap based on dark mode state
|
|
8
|
+
class ThemeToggle < Base
|
|
9
|
+
def initialize(variant: :ghost, size: :icon, **attrs)
|
|
10
|
+
@variant = variant
|
|
11
|
+
@size = size
|
|
12
|
+
@attrs = attrs
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def view_template(&block)
|
|
16
|
+
div(
|
|
17
|
+
data_controller: "shadcn--dark-mode",
|
|
18
|
+
data_shadcn__dark_mode_mode_value: "system"
|
|
19
|
+
) do
|
|
20
|
+
render Button.new(
|
|
21
|
+
variant: @variant,
|
|
22
|
+
size: @size,
|
|
23
|
+
data_action: "click->shadcn--dark-mode#toggle",
|
|
24
|
+
**@attrs
|
|
25
|
+
) do
|
|
26
|
+
if block_given?
|
|
27
|
+
yield
|
|
28
|
+
else
|
|
29
|
+
# Sun icon (visible in dark mode, triggers switch to light)
|
|
30
|
+
svg(
|
|
31
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
32
|
+
width: "16", height: "16",
|
|
33
|
+
viewbox: "0 0 24 24",
|
|
34
|
+
fill: "none",
|
|
35
|
+
stroke: "currentColor",
|
|
36
|
+
stroke_width: "2",
|
|
37
|
+
stroke_linecap: "round",
|
|
38
|
+
stroke_linejoin: "round",
|
|
39
|
+
class: "size-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
|
|
40
|
+
) do |s|
|
|
41
|
+
s.circle(cx: "12", cy: "12", r: "4")
|
|
42
|
+
s.path(d: "M12 2v2")
|
|
43
|
+
s.path(d: "M12 20v2")
|
|
44
|
+
s.path(d: "m4.93 4.93 1.41 1.41")
|
|
45
|
+
s.path(d: "m17.66 17.66 1.41 1.41")
|
|
46
|
+
s.path(d: "M2 12h2")
|
|
47
|
+
s.path(d: "M20 12h2")
|
|
48
|
+
s.path(d: "m6.34 17.66-1.41 1.41")
|
|
49
|
+
s.path(d: "m19.07 4.93-1.41 1.41")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Moon icon (visible in light mode, triggers switch to dark)
|
|
53
|
+
svg(
|
|
54
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
55
|
+
width: "16", height: "16",
|
|
56
|
+
viewbox: "0 0 24 24",
|
|
57
|
+
fill: "none",
|
|
58
|
+
stroke: "currentColor",
|
|
59
|
+
stroke_width: "2",
|
|
60
|
+
stroke_linecap: "round",
|
|
61
|
+
stroke_linejoin: "round",
|
|
62
|
+
class: "absolute size-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
|
|
63
|
+
) do |s|
|
|
64
|
+
s.path(d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
span(class: "sr-only") { "Toggle theme" }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Toggle
|
|
6
|
+
# Variants: default, outline
|
|
7
|
+
# Sizes: default, sm, lg
|
|
8
|
+
class Toggle < Base
|
|
9
|
+
VARIANTS = ClassVariants.build(
|
|
10
|
+
base: [
|
|
11
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap",
|
|
12
|
+
"transition-[color,box-shadow] outline-none",
|
|
13
|
+
"hover:bg-muted hover:text-muted-foreground",
|
|
14
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
15
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
16
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
17
|
+
"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
|
|
18
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
19
|
+
].join(" "),
|
|
20
|
+
variants: {
|
|
21
|
+
variant: {
|
|
22
|
+
default: "bg-transparent",
|
|
23
|
+
outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
default: "h-9 px-2 min-w-9",
|
|
27
|
+
sm: "h-8 px-1.5 min-w-8",
|
|
28
|
+
lg: "h-10 px-2.5 min-w-10"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
defaults: {
|
|
32
|
+
variant: :default,
|
|
33
|
+
size: :default
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def initialize(variant: :default, size: :default, pressed: false, **attrs)
|
|
38
|
+
@variant = variant
|
|
39
|
+
@size = size
|
|
40
|
+
@pressed = pressed
|
|
41
|
+
@attrs = attrs
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def view_template(&block)
|
|
45
|
+
button(**build_attrs, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def build_attrs
|
|
51
|
+
classes = cn(VARIANTS.render(variant: @variant, size: @size), @attrs.delete(:class))
|
|
52
|
+
@attrs.merge(
|
|
53
|
+
data_slot: "toggle",
|
|
54
|
+
data_controller: "shadcn--toggle",
|
|
55
|
+
data_shadcn__toggle_target: "button",
|
|
56
|
+
data_action: "click->shadcn--toggle#toggle",
|
|
57
|
+
data_shadcn__toggle_pressed_value: @pressed,
|
|
58
|
+
data_state: @pressed ? "on" : "off",
|
|
59
|
+
type: "button",
|
|
60
|
+
aria_pressed: @pressed,
|
|
61
|
+
class: classes
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui ToggleGroup
|
|
6
|
+
class ToggleGroup < Base
|
|
7
|
+
def initialize(variant: :default, size: :default, spacing: 1, **attrs)
|
|
8
|
+
@variant = variant
|
|
9
|
+
@size = size
|
|
10
|
+
@spacing = spacing
|
|
11
|
+
@attrs = attrs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template(&block)
|
|
15
|
+
div(**build_attrs, &block)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def build_attrs
|
|
21
|
+
spacing_classes = if @spacing == 0
|
|
22
|
+
"gap-0 -space-x-px [&_[data-slot=toggle]:first-child]:rounded-l-md [&_[data-slot=toggle]:last-child]:rounded-r-md [&_[data-slot=toggle]:not(:first-child):not(:last-child)]:rounded-none"
|
|
23
|
+
else
|
|
24
|
+
"gap-1"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
classes = cn(
|
|
28
|
+
"flex items-center",
|
|
29
|
+
spacing_classes,
|
|
30
|
+
@attrs.delete(:class)
|
|
31
|
+
)
|
|
32
|
+
@attrs.merge(
|
|
33
|
+
data_slot: "toggle-group",
|
|
34
|
+
data_controller: "shadcn--toggle-group",
|
|
35
|
+
data_shadcn__toggle_group_type_value: "single",
|
|
36
|
+
data_variant: @variant,
|
|
37
|
+
data_size: @size,
|
|
38
|
+
data_spacing: @spacing,
|
|
39
|
+
role: "group",
|
|
40
|
+
class: classes
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class ToggleGroupItem < Base
|
|
46
|
+
def initialize(value:, variant: :default, size: :default, pressed: false, **attrs)
|
|
47
|
+
@value = value
|
|
48
|
+
@variant = variant
|
|
49
|
+
@size = size
|
|
50
|
+
@pressed = pressed
|
|
51
|
+
@attrs = attrs
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def view_template(&block)
|
|
55
|
+
button(**build_attrs, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def build_attrs
|
|
61
|
+
classes = cn(Toggle::VARIANTS.render(variant: @variant, size: @size), @attrs.delete(:class))
|
|
62
|
+
@attrs.merge(
|
|
63
|
+
data_slot: "toggle-group-item",
|
|
64
|
+
data_shadcn__toggle_group_target: "item",
|
|
65
|
+
data_action: "click->shadcn--toggle-group#toggle keydown->shadcn--toggle-group#keydown",
|
|
66
|
+
data_value: @value,
|
|
67
|
+
data_state: @pressed ? "on" : "off",
|
|
68
|
+
type: "button",
|
|
69
|
+
aria_pressed: @pressed,
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Tooltip
|
|
6
|
+
class Tooltip < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
div(**build_attrs, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_attrs
|
|
18
|
+
@attrs.merge(
|
|
19
|
+
data_slot: "tooltip",
|
|
20
|
+
data_controller: "shadcn--tooltip"
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class TooltipTrigger < Base
|
|
26
|
+
def initialize(**attrs)
|
|
27
|
+
@attrs = attrs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def view_template(&block)
|
|
31
|
+
div(**build_attrs, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def build_attrs
|
|
37
|
+
@attrs.merge(
|
|
38
|
+
data_slot: "tooltip-trigger",
|
|
39
|
+
data_shadcn__tooltip_target: "trigger",
|
|
40
|
+
data_action: "mouseenter->shadcn--tooltip#mouseEnter mouseleave->shadcn--tooltip#mouseLeave focusin->shadcn--tooltip#focusIn focusout->shadcn--tooltip#focusOut",
|
|
41
|
+
role: "button",
|
|
42
|
+
style: "display: inline-block"
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class TooltipContent < Base
|
|
48
|
+
def initialize(**attrs)
|
|
49
|
+
@attrs = attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def view_template(&block)
|
|
53
|
+
div(**build_attrs, &block)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_attrs
|
|
59
|
+
classes = cn(
|
|
60
|
+
"z-50 w-fit rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background",
|
|
61
|
+
"animate-in fade-in-0 zoom-in-95",
|
|
62
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
63
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
64
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
65
|
+
@attrs.delete(:class)
|
|
66
|
+
)
|
|
67
|
+
@attrs.merge(
|
|
68
|
+
data_slot: "tooltip-content",
|
|
69
|
+
data_shadcn__tooltip_target: "content",
|
|
70
|
+
data_action: "mouseenter->shadcn--tooltip#contentMouseEnter mouseleave->shadcn--tooltip#contentMouseLeave",
|
|
71
|
+
role: "tooltip",
|
|
72
|
+
hidden: true,
|
|
73
|
+
class: classes
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Typography components
|
|
6
|
+
# Heading, Paragraph, Blockquote, InlineCode, Lead, Muted, etc.
|
|
7
|
+
|
|
8
|
+
class TypographyH1 < Base
|
|
9
|
+
def initialize(**attrs)
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
h1(**build_attrs, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def build_attrs
|
|
20
|
+
classes = cn("scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl", @attrs.delete(:class))
|
|
21
|
+
@attrs.merge(data_slot: "typography-h1", class: classes)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class TypographyH2 < Base
|
|
26
|
+
def initialize(**attrs)
|
|
27
|
+
@attrs = attrs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def view_template(&block)
|
|
31
|
+
h2(**build_attrs, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def build_attrs
|
|
37
|
+
classes = cn("scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0", @attrs.delete(:class))
|
|
38
|
+
@attrs.merge(data_slot: "typography-h2", class: classes)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class TypographyH3 < Base
|
|
43
|
+
def initialize(**attrs)
|
|
44
|
+
@attrs = attrs
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def view_template(&block)
|
|
48
|
+
h3(**build_attrs, &block)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_attrs
|
|
54
|
+
classes = cn("scroll-m-20 text-2xl font-semibold tracking-tight", @attrs.delete(:class))
|
|
55
|
+
@attrs.merge(data_slot: "typography-h3", class: classes)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class TypographyH4 < Base
|
|
60
|
+
def initialize(**attrs)
|
|
61
|
+
@attrs = attrs
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def view_template(&block)
|
|
65
|
+
h4(**build_attrs, &block)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def build_attrs
|
|
71
|
+
classes = cn("scroll-m-20 text-xl font-semibold tracking-tight", @attrs.delete(:class))
|
|
72
|
+
@attrs.merge(data_slot: "typography-h4", class: classes)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class TypographyP < Base
|
|
77
|
+
def initialize(**attrs)
|
|
78
|
+
@attrs = attrs
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def view_template(&block)
|
|
82
|
+
p(**build_attrs, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def build_attrs
|
|
88
|
+
classes = cn("leading-7 [&:not(:first-child)]:mt-6", @attrs.delete(:class))
|
|
89
|
+
@attrs.merge(data_slot: "typography-p", class: classes)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class TypographyBlockquote < Base
|
|
94
|
+
def initialize(**attrs)
|
|
95
|
+
@attrs = attrs
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def view_template(&block)
|
|
99
|
+
blockquote(**build_attrs, &block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def build_attrs
|
|
105
|
+
classes = cn("mt-6 border-l-2 pl-6 italic", @attrs.delete(:class))
|
|
106
|
+
@attrs.merge(data_slot: "typography-blockquote", class: classes)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class TypographyInlineCode < Base
|
|
111
|
+
def initialize(**attrs)
|
|
112
|
+
@attrs = attrs
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def view_template(&block)
|
|
116
|
+
code(**build_attrs, &block)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def build_attrs
|
|
122
|
+
classes = cn(
|
|
123
|
+
"relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold",
|
|
124
|
+
@attrs.delete(:class)
|
|
125
|
+
)
|
|
126
|
+
@attrs.merge(data_slot: "typography-inline-code", class: classes)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class TypographyLead < Base
|
|
131
|
+
def initialize(**attrs)
|
|
132
|
+
@attrs = attrs
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def view_template(&block)
|
|
136
|
+
p(**build_attrs, &block)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def build_attrs
|
|
142
|
+
classes = cn("text-xl text-muted-foreground", @attrs.delete(:class))
|
|
143
|
+
@attrs.merge(data_slot: "typography-lead", class: classes)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class TypographyLarge < Base
|
|
148
|
+
def initialize(**attrs)
|
|
149
|
+
@attrs = attrs
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def view_template(&block)
|
|
153
|
+
div(**build_attrs, &block)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
def build_attrs
|
|
159
|
+
classes = cn("text-lg font-semibold", @attrs.delete(:class))
|
|
160
|
+
@attrs.merge(data_slot: "typography-large", class: classes)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class TypographySmall < Base
|
|
165
|
+
def initialize(**attrs)
|
|
166
|
+
@attrs = attrs
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def view_template(&block)
|
|
170
|
+
small(**build_attrs, &block)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
|
|
175
|
+
def build_attrs
|
|
176
|
+
classes = cn("text-sm font-medium leading-none", @attrs.delete(:class))
|
|
177
|
+
@attrs.merge(data_slot: "typography-small", class: classes)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
class TypographyMuted < Base
|
|
182
|
+
def initialize(**attrs)
|
|
183
|
+
@attrs = attrs
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def view_template(&block)
|
|
187
|
+
p(**build_attrs, &block)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
|
|
192
|
+
def build_attrs
|
|
193
|
+
classes = cn("text-sm text-muted-foreground", @attrs.delete(:class))
|
|
194
|
+
@attrs.merge(data_slot: "typography-muted", class: classes)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class TypographyList < Base
|
|
199
|
+
def initialize(ordered: false, **attrs)
|
|
200
|
+
@ordered = ordered
|
|
201
|
+
@attrs = attrs
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def view_template(&block)
|
|
205
|
+
tag = @ordered ? :ol : :ul
|
|
206
|
+
send(tag, **build_attrs, &block)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
def build_attrs
|
|
212
|
+
classes = cn("my-6 ml-6 list-disc [&>li]:mt-2", @attrs.delete(:class))
|
|
213
|
+
@attrs.merge(data_slot: "typography-list", class: classes)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
data/lib/shadcn-phlex.rb
ADDED