view_primitives 0.1.3 → 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 +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +57 -2
- data/lib/generators/view_primitives/add/add_generator.rb +8 -62
- data/lib/generators/view_primitives/add/templates/accordion/accordion_item_component.rb.tt +30 -11
- data/lib/generators/view_primitives/add/templates/alert/alert_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/alert_dialog/alert_dialog_component.rb.tt +9 -9
- data/lib/generators/view_primitives/add/templates/aspect_ratio/aspect_ratio_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/audio/audio_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/avatar/avatar_component.rb.tt +8 -4
- data/lib/generators/view_primitives/add/templates/badge/badge_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/banner/banner_component.rb.tt +6 -6
- data/lib/generators/view_primitives/add/templates/bottom_nav/bottom_nav_component.rb.tt +11 -4
- data/lib/generators/view_primitives/add/templates/breadcrumb/breadcrumb_component.rb.tt +2 -2
- data/lib/generators/view_primitives/add/templates/button/button_component.rb.tt +8 -5
- data/lib/generators/view_primitives/add/templates/button_group/button_group_component.rb.tt +5 -5
- data/lib/generators/view_primitives/add/templates/calendar/calendar_component.rb.tt +18 -16
- data/lib/generators/view_primitives/add/templates/card/card_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/card/card_footer_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/carousel/carousel_component.rb.tt +26 -13
- data/lib/generators/view_primitives/add/templates/chart/chart_component.rb.tt +10 -4
- data/lib/generators/view_primitives/add/templates/chart/chart_controller.js +26 -3
- data/lib/generators/view_primitives/add/templates/chat_bubble/chat_bubble_component.rb.tt +4 -4
- data/lib/generators/view_primitives/add/templates/checkbox/checkbox_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/collapsible/collapsible_component.rb.tt +12 -5
- data/lib/generators/view_primitives/add/templates/combobox/combobox_component.rb.tt +3 -6
- data/lib/generators/view_primitives/add/templates/command/command_component.rb.tt +22 -18
- data/lib/generators/view_primitives/add/templates/command/command_controller.js +50 -0
- data/lib/generators/view_primitives/add/templates/context_menu/context_menu_component.rb.tt +9 -8
- data/lib/generators/view_primitives/add/templates/data_table/data_table_component.rb.tt +60 -29
- data/lib/generators/view_primitives/add/templates/data_table/data_table_controller.js +2 -2
- data/lib/generators/view_primitives/add/templates/date_picker/date_picker_component.rb.tt +8 -8
- data/lib/generators/view_primitives/add/templates/device_mockup/device_mockup_component.rb.tt +94 -21
- data/lib/generators/view_primitives/add/templates/dialog/dialog_component.rb.tt +13 -10
- data/lib/generators/view_primitives/add/templates/dialog/dialog_controller.js +52 -0
- data/lib/generators/view_primitives/add/templates/drawer/drawer_component.rb.tt +8 -7
- data/lib/generators/view_primitives/add/templates/dropdown_menu/dropdown_menu_component.rb.tt +5 -6
- data/lib/generators/view_primitives/add/templates/embed/embed_component.rb.tt +2 -2
- data/lib/generators/view_primitives/add/templates/figure/figure_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/file_input/file_input_component.rb.tt +3 -12
- data/lib/generators/view_primitives/add/templates/floating_label/floating_label_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/footer/footer_component.rb.tt +5 -4
- data/lib/generators/view_primitives/add/templates/form_field/form_field_component.rb.tt +18 -5
- data/lib/generators/view_primitives/add/templates/gallery/gallery_component.rb.tt +3 -3
- data/lib/generators/view_primitives/add/templates/gallery/gallery_controller.js +1 -1
- data/lib/generators/view_primitives/add/templates/hover_card/hover_card_component.rb.tt +6 -5
- data/lib/generators/view_primitives/add/templates/iframe/iframe_component.rb.tt +6 -4
- data/lib/generators/view_primitives/add/templates/image/image_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/indicator/indicator_component.rb.tt +5 -4
- data/lib/generators/view_primitives/add/templates/input/input_component.rb.tt +2 -13
- data/lib/generators/view_primitives/add/templates/input_otp/input_otp_component.rb.tt +22 -10
- data/lib/generators/view_primitives/add/templates/kbd/kbd_component.rb.tt +3 -1
- data/lib/generators/view_primitives/add/templates/list_group/list_group_component.rb.tt +6 -2
- data/lib/generators/view_primitives/add/templates/list_group/list_group_item_component.rb.tt +6 -4
- data/lib/generators/view_primitives/add/templates/map_area/map_area_component.rb.tt +3 -2
- data/lib/generators/view_primitives/add/templates/mega_menu/mega_menu_component.rb.tt +9 -9
- data/lib/generators/view_primitives/add/templates/menubar/menubar_component.rb.tt +5 -5
- data/lib/generators/view_primitives/add/templates/menubar/menubar_menu_component.rb.tt +4 -5
- data/lib/generators/view_primitives/add/templates/navbar/navbar_component.rb.tt +51 -11
- data/lib/generators/view_primitives/add/templates/navbar/navbar_controller.js +8 -3
- data/lib/generators/view_primitives/add/templates/navigation_menu/navigation_menu_component.rb.tt +12 -16
- data/lib/generators/view_primitives/add/templates/number_input/number_input_component.rb.tt +4 -11
- data/lib/generators/view_primitives/add/templates/pagination/pagination_component.rb.tt +4 -3
- data/lib/generators/view_primitives/add/templates/picture/picture_component.rb.tt +2 -1
- data/lib/generators/view_primitives/add/templates/popover/popover_component.rb.tt +1 -2
- data/lib/generators/view_primitives/add/templates/progress/progress_component.rb.tt +3 -1
- data/lib/generators/view_primitives/add/templates/qr_code/qr_code_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/radio_group/radio_group_component.rb.tt +8 -5
- data/lib/generators/view_primitives/add/templates/range/range_component.rb.tt +2 -3
- data/lib/generators/view_primitives/add/templates/rating/rating_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/rating_input/rating_controller.js +1 -1
- data/lib/generators/view_primitives/add/templates/rating_input/rating_input_component.rb.tt +4 -3
- data/lib/generators/view_primitives/add/templates/resizable/resizable_component.rb.tt +27 -15
- data/lib/generators/view_primitives/add/templates/scroll_area/scroll_area_component.rb.tt +10 -11
- data/lib/generators/view_primitives/add/templates/search_input/search_input_component.rb.tt +2 -11
- data/lib/generators/view_primitives/add/templates/select/select_component.rb.tt +25 -6
- data/lib/generators/view_primitives/add/templates/separator/separator_component.rb.tt +6 -3
- data/lib/generators/view_primitives/add/templates/sheet/sheet_component.rb.tt +25 -21
- data/lib/generators/view_primitives/add/templates/sidebar/sidebar_component.rb.tt +27 -21
- data/lib/generators/view_primitives/add/templates/skeleton/skeleton_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/speed_dial/speed_dial_component.rb.tt +8 -9
- data/lib/generators/view_primitives/add/templates/spinner/spinner_component.rb.tt +15 -6
- data/lib/generators/view_primitives/add/templates/stepper/stepper_component.rb.tt +17 -16
- data/lib/generators/view_primitives/add/templates/switch/switch_component.rb.tt +27 -14
- data/lib/generators/view_primitives/add/templates/tabs/tabs_component.html.erb +13 -7
- data/lib/generators/view_primitives/add/templates/tags_input/tags_input_component.rb.tt +136 -0
- data/lib/generators/view_primitives/add/templates/tags_input/tags_input_controller.js +90 -0
- data/lib/generators/view_primitives/add/templates/textarea/textarea_component.rb.tt +2 -11
- data/lib/generators/view_primitives/add/templates/timeline/timeline_component.rb.tt +9 -7
- data/lib/generators/view_primitives/add/templates/timepicker/timepicker_component.rb.tt +19 -15
- data/lib/generators/view_primitives/add/templates/toaster/toaster_component.rb.tt +10 -10
- data/lib/generators/view_primitives/add/templates/toaster/toaster_controller.js +6 -6
- data/lib/generators/view_primitives/add/templates/toggle/toggle_component.rb.tt +10 -3
- data/lib/generators/view_primitives/add/templates/toggle_group/toggle_group_component.rb.tt +6 -6
- data/lib/generators/view_primitives/add/templates/tooltip/tooltip_component.rb.tt +7 -6
- data/lib/generators/view_primitives/add/templates/video/video_component.rb.tt +1 -1
- data/lib/generators/view_primitives/add/templates/wysiwyg/wysiwyg_component.rb.tt +9 -3
- data/lib/generators/view_primitives/component_copier.rb +96 -0
- data/lib/generators/view_primitives/components.rb +16 -2
- data/lib/generators/view_primitives/install/install_generator.rb +13 -3
- data/lib/generators/view_primitives/install/templates/application_component.rb.tt +7 -0
- data/lib/generators/view_primitives/install/templates/styles.rb.tt +26 -0
- data/lib/generators/view_primitives/install/templates/view_primitives/themes/default.css +79 -0
- data/lib/generators/view_primitives/install/templates/view_primitives/themes/rose.css +57 -0
- data/lib/generators/view_primitives/install/templates/view_primitives/tokens.css +46 -0
- data/lib/generators/view_primitives/install/templates/view_primitives/utilities.css +64 -0
- data/lib/generators/view_primitives/install/templates/view_primitives.css +6 -66
- data/lib/generators/view_primitives/list/list_generator.rb +3 -1
- data/lib/generators/view_primitives/theme/theme_generator.rb +79 -0
- data/lib/generators/view_primitives/update/update_generator.rb +112 -0
- data/lib/view_primitives/class_helper.rb +4 -1
- data/lib/view_primitives/railtie.rb +1 -1
- data/lib/view_primitives/version.rb +1 -1
- metadata +12 -4
- data/lib/generators/view_primitives/add/templates/drawer/drawer_controller.js +0 -15
- data/lib/generators/view_primitives/add/templates/sheet/sheet_controller.js +0 -15
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module UI
|
|
4
4
|
class TooltipComponent < ApplicationComponent
|
|
5
|
-
BUBBLE_BASE = "absolute z-50 w-fit rounded-md px-3 py-1.5
|
|
6
|
-
"
|
|
7
|
-
"opacity-
|
|
8
|
-
"transition-opacity duration-200"
|
|
5
|
+
BUBBLE_BASE = "pointer-events-none absolute z-50 w-fit rounded-md bg-foreground px-3 py-1.5 " \
|
|
6
|
+
"text-xs text-balance whitespace-nowrap text-background opacity-0 " \
|
|
7
|
+
"transition-opacity duration-200 group-hover:opacity-100"
|
|
9
8
|
|
|
10
9
|
POSITIONS = {
|
|
11
10
|
top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
@@ -23,7 +22,8 @@ module UI
|
|
|
23
22
|
|
|
24
23
|
def call
|
|
25
24
|
content_tag(:span,
|
|
26
|
-
class: cn("relative inline-flex
|
|
25
|
+
class: cn("group relative inline-flex", @extra_class),
|
|
26
|
+
data: { slot: "tooltip" },
|
|
27
27
|
**@html_attrs) do
|
|
28
28
|
concat content
|
|
29
29
|
concat tooltip_bubble
|
|
@@ -36,7 +36,8 @@ module UI
|
|
|
36
36
|
content_tag(:span,
|
|
37
37
|
@text,
|
|
38
38
|
class: cn(BUBBLE_BASE, POSITIONS.fetch(@side, POSITIONS[:top])),
|
|
39
|
-
role: "tooltip"
|
|
39
|
+
role: "tooltip",
|
|
40
|
+
data: { slot: "tooltip-content" })
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|
|
@@ -30,7 +30,10 @@ module UI
|
|
|
30
30
|
|
|
31
31
|
ADAPTERS = %w[trix quill].freeze
|
|
32
32
|
|
|
33
|
-
WRAPPER_CLS = "rounded-md border border-input bg-
|
|
33
|
+
WRAPPER_CLS = "rounded-md border border-input bg-transparent shadow-xs dark:bg-input/30"
|
|
34
|
+
TRIX_CLS = "trix-content min-h-[200px] px-3 py-2 text-sm outline-none " \
|
|
35
|
+
"selection:bg-primary selection:text-primary-foreground " \
|
|
36
|
+
"#{UI::Styles::FOCUS_RING}"
|
|
34
37
|
|
|
35
38
|
def initialize(name:, adapter: :trix, value: nil, placeholder: nil,
|
|
36
39
|
toolbar: true, height: 200, **html_attrs)
|
|
@@ -46,7 +49,10 @@ module UI
|
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def call
|
|
49
|
-
content_tag(:div,
|
|
52
|
+
content_tag(:div,
|
|
53
|
+
class: cn(WRAPPER_CLS, @extra_class),
|
|
54
|
+
data: { slot: "wysiwyg" },
|
|
55
|
+
**@html_attrs) do
|
|
50
56
|
@adapter == "quill" ? quill_markup : trix_markup
|
|
51
57
|
end
|
|
52
58
|
end
|
|
@@ -59,7 +65,7 @@ module UI
|
|
|
59
65
|
tag.send(:"trix-editor",
|
|
60
66
|
input: @input_id,
|
|
61
67
|
placeholder: @placeholder,
|
|
62
|
-
class:
|
|
68
|
+
class: TRIX_CLS)
|
|
63
69
|
])
|
|
64
70
|
end
|
|
65
71
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "components"
|
|
4
|
+
|
|
5
|
+
module ViewPrimitives
|
|
6
|
+
module Generators
|
|
7
|
+
module ComponentCopier
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.no_tasks do
|
|
10
|
+
define_method(:template) do |source, *args, **options, &blk|
|
|
11
|
+
destination = args.first || options[:to]
|
|
12
|
+
return unless destination.nil? || confirm_overwrite(destination)
|
|
13
|
+
|
|
14
|
+
super(source, *args, **options, &blk)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
define_method(:copy_file) do |source, *args, **options|
|
|
18
|
+
destination = args.first || options[:to]
|
|
19
|
+
return unless destination.nil? || confirm_overwrite(destination)
|
|
20
|
+
|
|
21
|
+
super(source, *args, **options)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def copy_component(name)
|
|
29
|
+
dir = File.join(Components::TEMPLATE_ROOT, name)
|
|
30
|
+
unless File.directory?(dir)
|
|
31
|
+
say " Missing templates for #{name}", :red
|
|
32
|
+
return false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
with_source_root(Components::TEMPLATE_ROOT) do
|
|
36
|
+
Dir.each_child(dir).sort.each { |file| copy_template_file(name, file) }
|
|
37
|
+
end
|
|
38
|
+
copy_extra_stimulus(name)
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def copy_template_file(component, file)
|
|
43
|
+
relative = File.join(component, file)
|
|
44
|
+
|
|
45
|
+
case file
|
|
46
|
+
when /\.rb\.tt\z/
|
|
47
|
+
template relative, "app/components/ui/#{file.delete_suffix(".tt")}"
|
|
48
|
+
when /\.html\.erb\z/
|
|
49
|
+
copy_file relative, "app/components/ui/#{file}"
|
|
50
|
+
when /_controller\.js\z/
|
|
51
|
+
copy_js_controller("#{component}/#{file}", file.delete_suffix("_controller.js"))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def copy_extra_stimulus(name)
|
|
56
|
+
config = Components::EXTRA_STIMULUS[name]
|
|
57
|
+
return unless config
|
|
58
|
+
|
|
59
|
+
with_source_root(Components::TEMPLATE_ROOT) do
|
|
60
|
+
copy_js_controller(config[:source], config[:name])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def copy_js_controller(relative_source, stimulus_name)
|
|
65
|
+
return if stimulus_name.empty?
|
|
66
|
+
|
|
67
|
+
dir = js_controllers_dir
|
|
68
|
+
unless dir
|
|
69
|
+
say " Could not detect a JS controllers directory.", :yellow
|
|
70
|
+
say " Copy #{relative_source} manually and register Stimulus `#{stimulus_name}`.", :cyan
|
|
71
|
+
return
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
dest = "#{dir}/#{stimulus_name}_controller.js"
|
|
75
|
+
copy_file relative_source, dest
|
|
76
|
+
say " Stimulus `#{stimulus_name}` → #{dest}", :green
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def with_source_root(path)
|
|
80
|
+
already_present = source_paths.include?(path)
|
|
81
|
+
source_paths.unshift(path) unless already_present
|
|
82
|
+
yield
|
|
83
|
+
ensure
|
|
84
|
+
source_paths.delete(path) unless already_present
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def confirm_overwrite(destination)
|
|
88
|
+
return true unless File.exist?(File.join(destination_root, destination))
|
|
89
|
+
return true if options[:force]
|
|
90
|
+
|
|
91
|
+
say " #{destination} already exists.", :yellow
|
|
92
|
+
yes?(" Overwrite? [y/N] ")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -7,9 +7,13 @@ module ViewPrimitives
|
|
|
7
7
|
|
|
8
8
|
# Stimulus controllers not colocated with the component template directory.
|
|
9
9
|
EXTRA_STIMULUS = {
|
|
10
|
-
"alert_dialog" => {source: "dialog/dialog_controller.js", name: "dialog"}
|
|
10
|
+
"alert_dialog" => {source: "dialog/dialog_controller.js", name: "dialog"},
|
|
11
|
+
"sheet" => {source: "dialog/dialog_controller.js", name: "dialog"},
|
|
12
|
+
"drawer" => {source: "dialog/dialog_controller.js", name: "dialog"}
|
|
11
13
|
}.freeze
|
|
12
14
|
|
|
15
|
+
OPTIONAL_THEMES = %w[rose].freeze
|
|
16
|
+
|
|
13
17
|
# Post-install instructions for components that require external dependencies.
|
|
14
18
|
SETUP_NOTES = {
|
|
15
19
|
"chart" => <<~TEXT,
|
|
@@ -47,7 +51,9 @@ module ViewPrimitives
|
|
|
47
51
|
}.freeze
|
|
48
52
|
|
|
49
53
|
def self.supported
|
|
50
|
-
@supported ||= Dir.children(TEMPLATE_ROOT)
|
|
54
|
+
@supported ||= Dir.children(TEMPLATE_ROOT)
|
|
55
|
+
.select { |e| File.directory?(File.join(TEMPLATE_ROOT, e)) }
|
|
56
|
+
.sort.freeze
|
|
51
57
|
end
|
|
52
58
|
|
|
53
59
|
def self.primary_path(component)
|
|
@@ -57,6 +63,14 @@ module ViewPrimitives
|
|
|
57
63
|
def self.installed?(component, root)
|
|
58
64
|
File.exist?(File.join(root, primary_path(component)))
|
|
59
65
|
end
|
|
66
|
+
|
|
67
|
+
def self.installed(root)
|
|
68
|
+
supported.select { |name| installed?(name, root) }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.optional_theme_imports
|
|
72
|
+
OPTIONAL_THEMES
|
|
73
|
+
end
|
|
60
74
|
end
|
|
61
75
|
end
|
|
62
76
|
end
|
|
@@ -9,6 +9,9 @@ module ViewPrimitives
|
|
|
9
9
|
|
|
10
10
|
source_root File.expand_path("templates", __dir__)
|
|
11
11
|
|
|
12
|
+
class_option :force, type: :boolean, default: false,
|
|
13
|
+
desc: "Overwrite existing ApplicationComponent and CSS files"
|
|
14
|
+
|
|
12
15
|
def verify_ui_inflection
|
|
13
16
|
return if "ui/button_component".camelize == "UI::ButtonComponent"
|
|
14
17
|
|
|
@@ -19,16 +22,23 @@ module ViewPrimitives
|
|
|
19
22
|
|
|
20
23
|
def create_application_component
|
|
21
24
|
target = "app/components/application_component.rb"
|
|
25
|
+
exists = File.exist?(File.join(destination_root, target))
|
|
22
26
|
|
|
23
|
-
if
|
|
24
|
-
say " ApplicationComponent already exists. Add `include ViewPrimitives::ClassHelper`
|
|
27
|
+
if exists && !options[:force]
|
|
28
|
+
say " ApplicationComponent already exists. Add `include ViewPrimitives::ClassHelper` and " \
|
|
29
|
+
"`extract_html_attrs` manually, or re-run with --force.", :yellow
|
|
25
30
|
else
|
|
26
31
|
template "application_component.rb.tt", target
|
|
27
32
|
end
|
|
28
33
|
end
|
|
29
34
|
|
|
30
|
-
def
|
|
35
|
+
def create_ui_styles
|
|
36
|
+
template "styles.rb.tt", "app/components/ui/styles.rb"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_css_bundle
|
|
31
40
|
copy_file "view_primitives.css", css_dest_path
|
|
41
|
+
directory "view_primitives", "#{css_dest_dir}/view_primitives"
|
|
32
42
|
end
|
|
33
43
|
|
|
34
44
|
def inject_css_import
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UI
|
|
4
|
+
# Shared CSS primitive class names (see view_primitives/utilities.css).
|
|
5
|
+
# Update via: rails g view_primitives:update --skip-components
|
|
6
|
+
module Styles
|
|
7
|
+
FOCUS_RING = "vp-focus-ring"
|
|
8
|
+
PEER_FOCUS_RING = "vp-peer-focus-ring"
|
|
9
|
+
BORDER = "vp-border"
|
|
10
|
+
INPUT = "vp-input"
|
|
11
|
+
TEXTAREA = "vp-textarea"
|
|
12
|
+
SELECT = "vp-select"
|
|
13
|
+
OVERLAY = "vp-overlay"
|
|
14
|
+
POPOVER_PANEL = "vp-popover-panel"
|
|
15
|
+
MENU_ITEM = "vp-menu-item"
|
|
16
|
+
MENU_SEPARATOR = "block -mx-1 my-1 h-px shrink-0 border-0 bg-border"
|
|
17
|
+
# Form-adjacent surfaces (calendar, picker popovers)
|
|
18
|
+
FIELD_PANEL = "rounded-md border border-input bg-background shadow-xs"
|
|
19
|
+
# Date/time picker trigger button shell (add width via class:)
|
|
20
|
+
PICKER_TRIGGER = "inline-flex h-9 shrink-0 cursor-pointer items-center justify-start gap-2 " \
|
|
21
|
+
"rounded-md border border-input bg-background px-4 py-2 text-left text-sm font-normal shadow-xs " \
|
|
22
|
+
"transition-all outline-none hover:bg-accent hover:text-accent-foreground " \
|
|
23
|
+
"#{FOCUS_RING} dark:bg-input/30 dark:hover:bg-input/50 " \
|
|
24
|
+
"aria-expanded:border-ring has-[>svg]:px-3"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Default light and dark theme values. Override per-theme in themes/*.css. */
|
|
2
|
+
:root {
|
|
3
|
+
--radius: 0.625rem;
|
|
4
|
+
--background: oklch(1 0 0);
|
|
5
|
+
--foreground: oklch(0.145 0 0);
|
|
6
|
+
--card: oklch(1 0 0);
|
|
7
|
+
--card-foreground: oklch(0.145 0 0);
|
|
8
|
+
--popover: oklch(1 0 0);
|
|
9
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
10
|
+
--primary: oklch(0.205 0 0);
|
|
11
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
12
|
+
--secondary: oklch(0.97 0 0);
|
|
13
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
14
|
+
--muted: oklch(0.97 0 0);
|
|
15
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
16
|
+
--accent: oklch(0.97 0 0);
|
|
17
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
18
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
19
|
+
--destructive-foreground: oklch(0.97 0.01 17);
|
|
20
|
+
--border: oklch(0.922 0 0);
|
|
21
|
+
--input: oklch(0.922 0 0);
|
|
22
|
+
--ring: oklch(0.708 0 0);
|
|
23
|
+
--chart-1: oklch(0.809 0.105 251.813);
|
|
24
|
+
--chart-2: oklch(0.623 0.214 259.815);
|
|
25
|
+
--chart-3: oklch(0.546 0.245 262.881);
|
|
26
|
+
--chart-4: oklch(0.488 0.243 264.376);
|
|
27
|
+
--chart-5: oklch(0.424 0.199 265.638);
|
|
28
|
+
--sidebar: oklch(0.985 0 0);
|
|
29
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
30
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
31
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
32
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
33
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
34
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
35
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
36
|
+
--surface: oklch(0.98 0 0);
|
|
37
|
+
--surface-foreground: var(--foreground);
|
|
38
|
+
--selection: oklch(0.205 0 0);
|
|
39
|
+
--selection-foreground: oklch(1 0 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.dark {
|
|
43
|
+
--background: oklch(0.145 0 0);
|
|
44
|
+
--foreground: oklch(0.985 0 0);
|
|
45
|
+
--card: oklch(0.205 0 0);
|
|
46
|
+
--card-foreground: oklch(0.985 0 0);
|
|
47
|
+
--popover: oklch(0.205 0 0);
|
|
48
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
49
|
+
--primary: oklch(0.922 0 0);
|
|
50
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
51
|
+
--secondary: oklch(0.269 0 0);
|
|
52
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
53
|
+
--muted: oklch(0.269 0 0);
|
|
54
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
55
|
+
--accent: oklch(0.371 0 0);
|
|
56
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
57
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
58
|
+
--destructive-foreground: oklch(0.58 0.22 27);
|
|
59
|
+
--border: oklch(1 0 0 / 10%);
|
|
60
|
+
--input: oklch(1 0 0 / 15%);
|
|
61
|
+
--ring: oklch(0.556 0 0);
|
|
62
|
+
--chart-1: oklch(0.809 0.105 251.813);
|
|
63
|
+
--chart-2: oklch(0.623 0.214 259.815);
|
|
64
|
+
--chart-3: oklch(0.546 0.245 262.881);
|
|
65
|
+
--chart-4: oklch(0.488 0.243 264.376);
|
|
66
|
+
--chart-5: oklch(0.424 0.199 265.638);
|
|
67
|
+
--sidebar: oklch(0.205 0 0);
|
|
68
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
69
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
70
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
71
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
72
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
73
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
74
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
75
|
+
--surface: oklch(0.2 0 0);
|
|
76
|
+
--surface-foreground: oklch(0.708 0 0);
|
|
77
|
+
--selection: oklch(0.922 0 0);
|
|
78
|
+
--selection-foreground: oklch(0.205 0 0);
|
|
79
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/* Rose theme — apply with data-theme="rose" on html or a layout wrapper. */
|
|
2
|
+
[data-theme="rose"] {
|
|
3
|
+
--radius: 0.75rem;
|
|
4
|
+
--background: oklch(0.99 0.01 350);
|
|
5
|
+
--foreground: oklch(0.25 0.04 350);
|
|
6
|
+
--card: oklch(1 0.005 350);
|
|
7
|
+
--card-foreground: oklch(0.25 0.04 350);
|
|
8
|
+
--popover: oklch(1 0.005 350);
|
|
9
|
+
--popover-foreground: oklch(0.25 0.04 350);
|
|
10
|
+
--primary: oklch(0.55 0.2 350);
|
|
11
|
+
--primary-foreground: oklch(0.99 0.01 350);
|
|
12
|
+
--secondary: oklch(0.96 0.02 350);
|
|
13
|
+
--secondary-foreground: oklch(0.35 0.06 350);
|
|
14
|
+
--muted: oklch(0.96 0.02 350);
|
|
15
|
+
--muted-foreground: oklch(0.5 0.05 350);
|
|
16
|
+
--accent: oklch(0.94 0.03 350);
|
|
17
|
+
--accent-foreground: oklch(0.35 0.06 350);
|
|
18
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
19
|
+
--destructive-foreground: oklch(0.97 0.01 17);
|
|
20
|
+
--border: oklch(0.9 0.03 350);
|
|
21
|
+
--input: oklch(0.9 0.03 350);
|
|
22
|
+
--ring: oklch(0.55 0.2 350);
|
|
23
|
+
--chart-1: oklch(0.65 0.22 350);
|
|
24
|
+
--chart-2: oklch(0.6 0.18 10);
|
|
25
|
+
--chart-3: oklch(0.55 0.15 320);
|
|
26
|
+
--chart-4: oklch(0.7 0.12 280);
|
|
27
|
+
--chart-5: oklch(0.5 0.1 200);
|
|
28
|
+
--sidebar: oklch(0.98 0.015 350);
|
|
29
|
+
--sidebar-foreground: oklch(0.25 0.04 350);
|
|
30
|
+
--sidebar-primary: oklch(0.55 0.2 350);
|
|
31
|
+
--sidebar-primary-foreground: oklch(0.99 0.01 350);
|
|
32
|
+
--sidebar-accent: oklch(0.94 0.03 350);
|
|
33
|
+
--sidebar-accent-foreground: oklch(0.35 0.06 350);
|
|
34
|
+
--sidebar-border: oklch(0.9 0.03 350);
|
|
35
|
+
--sidebar-ring: oklch(0.55 0.2 350);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[data-theme="rose"].dark,
|
|
39
|
+
.dark [data-theme="rose"] {
|
|
40
|
+
--background: oklch(0.18 0.03 350);
|
|
41
|
+
--foreground: oklch(0.97 0.01 350);
|
|
42
|
+
--card: oklch(0.22 0.04 350);
|
|
43
|
+
--card-foreground: oklch(0.97 0.01 350);
|
|
44
|
+
--popover: oklch(0.22 0.04 350);
|
|
45
|
+
--popover-foreground: oklch(0.97 0.01 350);
|
|
46
|
+
--primary: oklch(0.72 0.18 350);
|
|
47
|
+
--primary-foreground: oklch(0.2 0.04 350);
|
|
48
|
+
--secondary: oklch(0.28 0.04 350);
|
|
49
|
+
--secondary-foreground: oklch(0.97 0.01 350);
|
|
50
|
+
--muted: oklch(0.28 0.04 350);
|
|
51
|
+
--muted-foreground: oklch(0.7 0.05 350);
|
|
52
|
+
--accent: oklch(0.32 0.05 350);
|
|
53
|
+
--accent-foreground: oklch(0.97 0.01 350);
|
|
54
|
+
--border: oklch(1 0 0 / 12%);
|
|
55
|
+
--input: oklch(1 0 0 / 16%);
|
|
56
|
+
--ring: oklch(0.72 0.18 350);
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* Design token bridge — maps CSS variables to Tailwind theme keys. */
|
|
2
|
+
@theme inline {
|
|
3
|
+
--color-background: var(--background);
|
|
4
|
+
--color-foreground: var(--foreground);
|
|
5
|
+
--color-card: var(--card);
|
|
6
|
+
--color-card-foreground: var(--card-foreground);
|
|
7
|
+
--color-popover: var(--popover);
|
|
8
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
9
|
+
--color-primary: var(--primary);
|
|
10
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
11
|
+
--color-secondary: var(--secondary);
|
|
12
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
13
|
+
--color-muted: var(--muted);
|
|
14
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
15
|
+
--color-accent: var(--accent);
|
|
16
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
17
|
+
--color-destructive: var(--destructive);
|
|
18
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
19
|
+
--color-border: var(--border);
|
|
20
|
+
--color-input: var(--input);
|
|
21
|
+
--color-ring: var(--ring);
|
|
22
|
+
--color-chart-1: var(--chart-1);
|
|
23
|
+
--color-chart-2: var(--chart-2);
|
|
24
|
+
--color-chart-3: var(--chart-3);
|
|
25
|
+
--color-chart-4: var(--chart-4);
|
|
26
|
+
--color-chart-5: var(--chart-5);
|
|
27
|
+
--color-sidebar: var(--sidebar);
|
|
28
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
29
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
30
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
31
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
32
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
33
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
34
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
35
|
+
--color-surface: var(--surface);
|
|
36
|
+
--color-surface-foreground: var(--surface-foreground);
|
|
37
|
+
--color-selection: var(--selection);
|
|
38
|
+
--color-selection-foreground: var(--selection-foreground);
|
|
39
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
40
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
41
|
+
--radius-lg: var(--radius);
|
|
42
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
43
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
44
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
45
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
46
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* Shared component primitives — referenced from UI::Styles in Ruby components. */
|
|
2
|
+
|
|
3
|
+
@utility vp-focus-ring {
|
|
4
|
+
@apply outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@utility vp-input {
|
|
8
|
+
@apply h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs;
|
|
9
|
+
@apply transition-[color,box-shadow] outline-none;
|
|
10
|
+
@apply selection:bg-primary selection:text-primary-foreground;
|
|
11
|
+
@apply file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground;
|
|
12
|
+
@apply placeholder:text-muted-foreground;
|
|
13
|
+
@apply vp-focus-ring;
|
|
14
|
+
@apply aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40;
|
|
15
|
+
@apply disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
|
16
|
+
@apply md:text-sm dark:bg-input/30;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@utility vp-textarea {
|
|
20
|
+
@apply field-sizing-content min-h-16 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs;
|
|
21
|
+
@apply transition-[color,box-shadow] outline-none;
|
|
22
|
+
@apply selection:bg-primary selection:text-primary-foreground;
|
|
23
|
+
@apply placeholder:text-muted-foreground;
|
|
24
|
+
@apply vp-focus-ring;
|
|
25
|
+
@apply aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40;
|
|
26
|
+
@apply disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
|
27
|
+
@apply md:text-sm dark:bg-input/30;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@utility vp-overlay {
|
|
31
|
+
@apply fixed inset-0 z-50 bg-black/50;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@utility vp-border {
|
|
35
|
+
@apply border border-border;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@utility vp-popover-panel {
|
|
39
|
+
@apply absolute z-50 rounded-md border border-border bg-popover text-popover-foreground shadow-xs outline-hidden;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@utility vp-select {
|
|
43
|
+
@apply h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent px-3 py-2 pr-9 text-sm shadow-xs;
|
|
44
|
+
@apply transition-[color,box-shadow] outline-none;
|
|
45
|
+
@apply selection:bg-primary selection:text-primary-foreground;
|
|
46
|
+
@apply placeholder:text-muted-foreground;
|
|
47
|
+
@apply vp-focus-ring;
|
|
48
|
+
@apply aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40;
|
|
49
|
+
@apply disabled:pointer-events-none disabled:cursor-not-allowed;
|
|
50
|
+
@apply data-[size=sm]:h-8 data-[size=sm]:py-1;
|
|
51
|
+
@apply dark:bg-input/30 dark:hover:bg-input/50;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@utility vp-peer-focus-ring {
|
|
55
|
+
@apply peer-focus-visible:border-ring peer-focus-visible:ring-[3px] peer-focus-visible:ring-ring/50;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@utility vp-menu-item {
|
|
59
|
+
@apply relative flex cursor-default items-center gap-2 whitespace-nowrap rounded-sm px-2 py-1.5 text-sm outline-hidden select-none;
|
|
60
|
+
@apply transition-colors;
|
|
61
|
+
@apply focus:bg-accent focus:text-accent-foreground;
|
|
62
|
+
@apply data-[disabled]:pointer-events-none data-[disabled]:opacity-50;
|
|
63
|
+
@apply [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
|
64
|
+
}
|
|
@@ -1,67 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
--color-card-foreground: var(--card-foreground);
|
|
6
|
-
--color-popover: var(--popover);
|
|
7
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
8
|
-
--color-primary: var(--primary);
|
|
9
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
10
|
-
--color-secondary: var(--secondary);
|
|
11
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
12
|
-
--color-muted: var(--muted);
|
|
13
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
14
|
-
--color-accent: var(--accent);
|
|
15
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
16
|
-
--color-destructive: var(--destructive);
|
|
17
|
-
--color-border: var(--border);
|
|
18
|
-
--color-input: var(--input);
|
|
19
|
-
--color-ring: var(--ring);
|
|
20
|
-
--radius-sm: calc(var(--radius) - 4px);
|
|
21
|
-
--radius-md: calc(var(--radius) - 2px);
|
|
22
|
-
--radius-lg: var(--radius);
|
|
23
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
24
|
-
}
|
|
1
|
+
/* ViewPrimitives design system entry point. */
|
|
2
|
+
@import "./view_primitives/tokens.css";
|
|
3
|
+
@import "./view_primitives/utilities.css";
|
|
4
|
+
@import "./view_primitives/themes/default.css";
|
|
25
5
|
|
|
26
|
-
:
|
|
27
|
-
|
|
28
|
-
--foreground: oklch(0.145 0 0);
|
|
29
|
-
--card: oklch(1 0 0);
|
|
30
|
-
--card-foreground: oklch(0.145 0 0);
|
|
31
|
-
--popover: oklch(1 0 0);
|
|
32
|
-
--popover-foreground: oklch(0.145 0 0);
|
|
33
|
-
--primary: oklch(0.205 0 0);
|
|
34
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
35
|
-
--secondary: oklch(0.97 0 0);
|
|
36
|
-
--secondary-foreground: oklch(0.205 0 0);
|
|
37
|
-
--muted: oklch(0.97 0 0);
|
|
38
|
-
--muted-foreground: oklch(0.556 0 0);
|
|
39
|
-
--accent: oklch(0.97 0 0);
|
|
40
|
-
--accent-foreground: oklch(0.205 0 0);
|
|
41
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
42
|
-
--border: oklch(0.922 0 0);
|
|
43
|
-
--input: oklch(0.922 0 0);
|
|
44
|
-
--ring: oklch(0.708 0 0);
|
|
45
|
-
--radius: 0.625rem;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.dark {
|
|
49
|
-
--background: oklch(0.145 0 0);
|
|
50
|
-
--foreground: oklch(0.985 0 0);
|
|
51
|
-
--card: oklch(0.205 0 0);
|
|
52
|
-
--card-foreground: oklch(0.985 0 0);
|
|
53
|
-
--popover: oklch(0.205 0 0);
|
|
54
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
55
|
-
--primary: oklch(0.922 0 0);
|
|
56
|
-
--primary-foreground: oklch(0.205 0 0);
|
|
57
|
-
--secondary: oklch(0.269 0 0);
|
|
58
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
59
|
-
--muted: oklch(0.269 0 0);
|
|
60
|
-
--muted-foreground: oklch(0.708 0 0);
|
|
61
|
-
--accent: oklch(0.269 0 0);
|
|
62
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
63
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
64
|
-
--border: oklch(1 0 0 / 10%);
|
|
65
|
-
--input: oklch(1 0 0 / 15%);
|
|
66
|
-
--ring: oklch(0.556 0 0);
|
|
67
|
-
}
|
|
6
|
+
/* Optional themes — install with: rails g view_primitives:theme rose */
|
|
7
|
+
/* @import "./view_primitives/themes/rose.css"; */
|
|
@@ -18,7 +18,9 @@ module ViewPrimitives
|
|
|
18
18
|
say format("%-18s %s", component, status), color
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
say "\nInstall:
|
|
21
|
+
say "\nInstall: rails g view_primitives:add <name>", :cyan
|
|
22
|
+
say "Update: rails g view_primitives:update", :cyan
|
|
23
|
+
say "Themes: rails g view_primitives:theme rose\n", :cyan
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
end
|