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,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Command (cmdk-style command palette)
|
|
6
|
+
# Wired to shadcn--command Stimulus controller
|
|
7
|
+
class Command < Base
|
|
8
|
+
def initialize(**attrs)
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&block)
|
|
13
|
+
div(**build_attrs, &block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def build_attrs
|
|
19
|
+
classes = cn(
|
|
20
|
+
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
|
21
|
+
@attrs.delete(:class)
|
|
22
|
+
)
|
|
23
|
+
@attrs.merge(
|
|
24
|
+
data_slot: "command",
|
|
25
|
+
data_controller: "shadcn--command",
|
|
26
|
+
class: classes
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class CommandDialog < Base
|
|
32
|
+
def initialize(**attrs)
|
|
33
|
+
@attrs = attrs
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def view_template(&block)
|
|
37
|
+
# Overlay
|
|
38
|
+
div(
|
|
39
|
+
data_slot: "command-overlay",
|
|
40
|
+
data_shadcn__command_target: "overlay",
|
|
41
|
+
data_action: "click->shadcn--command#clickOverlay",
|
|
42
|
+
class: "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
43
|
+
hidden: true
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Dialog
|
|
47
|
+
div(**build_attrs, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def build_attrs
|
|
53
|
+
classes = cn(
|
|
54
|
+
"fixed top-[50%] left-[50%] z-50 w-full max-w-lg translate-x-[-50%] translate-y-[-50%]",
|
|
55
|
+
"overflow-hidden rounded-lg border bg-popover p-0 shadow-lg",
|
|
56
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
57
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
58
|
+
@attrs.delete(:class)
|
|
59
|
+
)
|
|
60
|
+
@attrs.merge(
|
|
61
|
+
data_slot: "command-dialog",
|
|
62
|
+
data_shadcn__command_target: "dialog",
|
|
63
|
+
hidden: true,
|
|
64
|
+
class: classes
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class CommandInput < Base
|
|
70
|
+
def initialize(placeholder: "Type a command or search...", **attrs)
|
|
71
|
+
@placeholder = placeholder
|
|
72
|
+
@attrs = attrs
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def view_template
|
|
76
|
+
div(class: "flex items-center border-b px-3") do
|
|
77
|
+
svg(xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
78
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
79
|
+
stroke_width: "2", class: "mr-2 size-4 shrink-0 opacity-50") do |s|
|
|
80
|
+
s.circle(cx: "11", cy: "11", r: "8")
|
|
81
|
+
s.path(d: "m21 21-4.3-4.3")
|
|
82
|
+
end
|
|
83
|
+
input(**build_attrs)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def build_attrs
|
|
90
|
+
classes = cn(
|
|
91
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none",
|
|
92
|
+
"placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
93
|
+
@attrs.delete(:class)
|
|
94
|
+
)
|
|
95
|
+
@attrs.merge(
|
|
96
|
+
data_slot: "command-input",
|
|
97
|
+
data_shadcn__command_target: "input",
|
|
98
|
+
data_action: "input->shadcn--command#filter",
|
|
99
|
+
type: "text",
|
|
100
|
+
placeholder: @placeholder,
|
|
101
|
+
class: classes
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class CommandList < Base
|
|
107
|
+
def initialize(**attrs)
|
|
108
|
+
@attrs = attrs
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def view_template(&block)
|
|
112
|
+
div(**build_attrs, &block)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def build_attrs
|
|
118
|
+
classes = cn(
|
|
119
|
+
"max-h-[300px] overflow-y-auto overflow-x-hidden",
|
|
120
|
+
@attrs.delete(:class)
|
|
121
|
+
)
|
|
122
|
+
@attrs.merge(
|
|
123
|
+
data_slot: "command-list",
|
|
124
|
+
data_shadcn__command_target: "list",
|
|
125
|
+
class: classes
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class CommandEmpty < Base
|
|
131
|
+
def initialize(**attrs)
|
|
132
|
+
@attrs = attrs
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def view_template(&block)
|
|
136
|
+
div(**build_attrs) do
|
|
137
|
+
if block_given?
|
|
138
|
+
yield
|
|
139
|
+
else
|
|
140
|
+
plain "No results found."
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def build_attrs
|
|
148
|
+
classes = cn("py-6 text-center text-sm", @attrs.delete(:class))
|
|
149
|
+
@attrs.merge(
|
|
150
|
+
data_slot: "command-empty",
|
|
151
|
+
data_shadcn__command_target: "empty",
|
|
152
|
+
hidden: true,
|
|
153
|
+
class: classes
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class CommandGroup < Base
|
|
159
|
+
def initialize(heading: nil, **attrs)
|
|
160
|
+
@heading = heading
|
|
161
|
+
@attrs = attrs
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def view_template(&block)
|
|
165
|
+
div(**build_attrs) do
|
|
166
|
+
if @heading
|
|
167
|
+
div(
|
|
168
|
+
data_slot: "command-group-heading",
|
|
169
|
+
class: "px-2 py-1.5 text-xs font-medium text-muted-foreground"
|
|
170
|
+
) { @heading }
|
|
171
|
+
end
|
|
172
|
+
div(data_slot: "command-group-items", class: "p-1", &block)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def build_attrs
|
|
179
|
+
classes = cn(
|
|
180
|
+
"overflow-hidden text-foreground [&_[data-slot=command-group-heading]]:px-2 [&_[data-slot=command-group-heading]]:py-1.5 [&_[data-slot=command-group-heading]]:text-xs [&_[data-slot=command-group-heading]]:font-medium [&_[data-slot=command-group-heading]]:text-muted-foreground",
|
|
181
|
+
@attrs.delete(:class)
|
|
182
|
+
)
|
|
183
|
+
@attrs.merge(
|
|
184
|
+
data_slot: "command-group",
|
|
185
|
+
data_shadcn__command_target: "group",
|
|
186
|
+
class: classes
|
|
187
|
+
)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class CommandItem < Base
|
|
192
|
+
def initialize(value: nil, **attrs)
|
|
193
|
+
@value = value
|
|
194
|
+
@attrs = attrs
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def view_template(&block)
|
|
198
|
+
div(**build_attrs, &block)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
def build_attrs
|
|
204
|
+
classes = cn(
|
|
205
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
206
|
+
"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
207
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
208
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
209
|
+
@attrs.delete(:class)
|
|
210
|
+
)
|
|
211
|
+
@attrs.merge(
|
|
212
|
+
data_slot: "command-item",
|
|
213
|
+
data_shadcn__command_target: "item",
|
|
214
|
+
data_action: "click->shadcn--command#selectItem",
|
|
215
|
+
data_value: @value,
|
|
216
|
+
tabindex: "-1",
|
|
217
|
+
class: classes
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
class CommandSeparator < Base
|
|
223
|
+
def initialize(**attrs)
|
|
224
|
+
@attrs = attrs
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def view_template
|
|
228
|
+
div(**build_attrs)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def build_attrs
|
|
234
|
+
classes = cn("-mx-1 h-px bg-border", @attrs.delete(:class))
|
|
235
|
+
@attrs.merge(data_slot: "command-separator", class: classes)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
class CommandShortcut < Base
|
|
240
|
+
def initialize(**attrs)
|
|
241
|
+
@attrs = attrs
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def view_template(&block)
|
|
245
|
+
span(**build_attrs, &block)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
private
|
|
249
|
+
|
|
250
|
+
def build_attrs
|
|
251
|
+
classes = cn("ml-auto text-xs tracking-widest text-muted-foreground", @attrs.delete(:class))
|
|
252
|
+
@attrs.merge(data_slot: "command-shortcut", class: classes)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui ContextMenu
|
|
6
|
+
class ContextMenu < 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: "context-menu",
|
|
20
|
+
data_controller: "shadcn--context-menu"
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ContextMenuTrigger < 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: "context-menu-trigger",
|
|
39
|
+
data_shadcn__context_menu_target: "trigger"
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class ContextMenuContent < Base
|
|
45
|
+
def initialize(**attrs)
|
|
46
|
+
@attrs = attrs
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def view_template(&block)
|
|
50
|
+
div(**build_attrs, &block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def build_attrs
|
|
56
|
+
classes = cn(
|
|
57
|
+
"z-50 max-h-[var(--radix-context-menu-content-available-height)] min-w-[8rem]",
|
|
58
|
+
"overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
59
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
60
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
61
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
62
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
63
|
+
@attrs.delete(:class)
|
|
64
|
+
)
|
|
65
|
+
@attrs.merge(
|
|
66
|
+
data_slot: "context-menu-content",
|
|
67
|
+
data_shadcn__context_menu_target: "content",
|
|
68
|
+
role: "menu",
|
|
69
|
+
hidden: true,
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class ContextMenuItem < Base
|
|
76
|
+
def initialize(inset: false, variant: :default, **attrs)
|
|
77
|
+
@inset = inset
|
|
78
|
+
@variant = variant
|
|
79
|
+
@attrs = attrs
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def view_template(&block)
|
|
83
|
+
div(**build_attrs, &block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def build_attrs
|
|
89
|
+
classes = cn(
|
|
90
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
91
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
92
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
93
|
+
"data-[inset]:pl-8",
|
|
94
|
+
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive",
|
|
95
|
+
"dark:data-[variant=destructive]:focus:bg-destructive/20",
|
|
96
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
97
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
98
|
+
@attrs.delete(:class)
|
|
99
|
+
)
|
|
100
|
+
result = @attrs.merge(
|
|
101
|
+
data_slot: "context-menu-item",
|
|
102
|
+
data_shadcn__context_menu_target: "item",
|
|
103
|
+
data_action: "click->shadcn--context-menu#selectItem",
|
|
104
|
+
data_variant: @variant,
|
|
105
|
+
role: "menuitem",
|
|
106
|
+
tabindex: "-1",
|
|
107
|
+
class: classes
|
|
108
|
+
)
|
|
109
|
+
result[:data_inset] = true if @inset
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class ContextMenuCheckboxItem < Base
|
|
115
|
+
def initialize(checked: false, **attrs)
|
|
116
|
+
@checked = checked
|
|
117
|
+
@attrs = attrs
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def view_template(&block)
|
|
121
|
+
div(**build_attrs) do
|
|
122
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
123
|
+
if @checked
|
|
124
|
+
svg(
|
|
125
|
+
xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
126
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
127
|
+
stroke_width: "2", class: "size-4"
|
|
128
|
+
) { |s| s.path(d: "M20 6 9 17l-5-5") }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
yield if block_given?
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def build_attrs
|
|
138
|
+
classes = cn(
|
|
139
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm",
|
|
140
|
+
"outline-hidden select-none focus:bg-accent focus:text-accent-foreground",
|
|
141
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
142
|
+
@attrs.delete(:class)
|
|
143
|
+
)
|
|
144
|
+
@attrs.merge(
|
|
145
|
+
data_slot: "context-menu-checkbox-item",
|
|
146
|
+
role: "menuitemcheckbox",
|
|
147
|
+
aria_checked: @checked,
|
|
148
|
+
class: classes
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class ContextMenuRadioGroup < Base
|
|
154
|
+
def initialize(**attrs)
|
|
155
|
+
@attrs = attrs
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def view_template(&block)
|
|
159
|
+
div(**build_attrs, &block)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def build_attrs
|
|
165
|
+
@attrs.merge(data_slot: "context-menu-radio-group", role: "group")
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class ContextMenuRadioItem < Base
|
|
170
|
+
def initialize(checked: false, **attrs)
|
|
171
|
+
@checked = checked
|
|
172
|
+
@attrs = attrs
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def view_template(&block)
|
|
176
|
+
div(**build_attrs) do
|
|
177
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
178
|
+
if @checked
|
|
179
|
+
svg(
|
|
180
|
+
xmlns: "http://www.w3.org/2000/svg", width: "8", height: "8",
|
|
181
|
+
viewbox: "0 0 24 24", fill: "currentColor", class: "size-2"
|
|
182
|
+
) { |s| s.circle(cx: "12", cy: "12", r: "10") }
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
yield if block_given?
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
def build_attrs
|
|
192
|
+
classes = cn(
|
|
193
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm",
|
|
194
|
+
"outline-hidden select-none focus:bg-accent focus:text-accent-foreground",
|
|
195
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
196
|
+
@attrs.delete(:class)
|
|
197
|
+
)
|
|
198
|
+
@attrs.merge(
|
|
199
|
+
data_slot: "context-menu-radio-item",
|
|
200
|
+
role: "menuitemradio",
|
|
201
|
+
aria_checked: @checked,
|
|
202
|
+
class: classes
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
class ContextMenuLabel < Base
|
|
208
|
+
def initialize(inset: false, **attrs)
|
|
209
|
+
@inset = inset
|
|
210
|
+
@attrs = attrs
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def view_template(&block)
|
|
214
|
+
div(**build_attrs, &block)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
private
|
|
218
|
+
|
|
219
|
+
def build_attrs
|
|
220
|
+
classes = cn("px-2 py-1.5 text-sm font-medium text-foreground data-[inset]:pl-8", @attrs.delete(:class))
|
|
221
|
+
result = @attrs.merge(data_slot: "context-menu-label", class: classes)
|
|
222
|
+
result[:data_inset] = true if @inset
|
|
223
|
+
result
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class ContextMenuSeparator < Base
|
|
228
|
+
def initialize(**attrs)
|
|
229
|
+
@attrs = attrs
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def view_template
|
|
233
|
+
div(**build_attrs)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
private
|
|
237
|
+
|
|
238
|
+
def build_attrs
|
|
239
|
+
classes = cn("-mx-1 my-1 h-px bg-border", @attrs.delete(:class))
|
|
240
|
+
@attrs.merge(data_slot: "context-menu-separator", role: "separator", class: classes)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
class ContextMenuShortcut < Base
|
|
245
|
+
def initialize(**attrs)
|
|
246
|
+
@attrs = attrs
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def view_template(&block)
|
|
250
|
+
span(**build_attrs, &block)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
def build_attrs
|
|
256
|
+
classes = cn("ml-auto text-xs tracking-widest text-muted-foreground", @attrs.delete(:class))
|
|
257
|
+
@attrs.merge(data_slot: "context-menu-shortcut", class: classes)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
class ContextMenuSubTrigger < Base
|
|
262
|
+
def initialize(inset: false, **attrs)
|
|
263
|
+
@inset = inset
|
|
264
|
+
@attrs = attrs
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def view_template(&block)
|
|
268
|
+
div(**build_attrs) do
|
|
269
|
+
yield if block_given?
|
|
270
|
+
svg(
|
|
271
|
+
xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
272
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
273
|
+
stroke_width: "2", class: "ml-auto size-4"
|
|
274
|
+
) { |s| s.path(d: "m9 18 6-6-6-6") }
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
private
|
|
279
|
+
|
|
280
|
+
def build_attrs
|
|
281
|
+
classes = cn(
|
|
282
|
+
"flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
283
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
284
|
+
"data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
285
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
286
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
287
|
+
@attrs.delete(:class)
|
|
288
|
+
)
|
|
289
|
+
result = @attrs.merge(data_slot: "context-menu-sub-trigger", class: classes)
|
|
290
|
+
result[:data_inset] = true if @inset
|
|
291
|
+
result
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
class ContextMenuSubContent < Base
|
|
296
|
+
def initialize(**attrs)
|
|
297
|
+
@attrs = attrs
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def view_template(&block)
|
|
301
|
+
div(**build_attrs, &block)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
private
|
|
305
|
+
|
|
306
|
+
def build_attrs
|
|
307
|
+
classes = cn(
|
|
308
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
|
|
309
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
310
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
311
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
312
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
313
|
+
@attrs.delete(:class)
|
|
314
|
+
)
|
|
315
|
+
@attrs.merge(data_slot: "context-menu-sub-content", class: classes)
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|