ruby_ui 1.1.0 → 1.3.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/lib/generators/ruby_ui/component_generator.rb +5 -1
- data/lib/generators/ruby_ui/dependencies.yml +32 -0
- data/lib/generators/ruby_ui/install/install_generator.rb +1 -1
- data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +1 -1
- data/lib/generators/ruby_ui/javascript_utils.rb +27 -6
- data/lib/ruby_ui/avatar/avatar.rb +3 -0
- data/lib/ruby_ui/avatar/avatar_controller.js +33 -0
- data/lib/ruby_ui/avatar/avatar_fallback.rb +3 -0
- data/lib/ruby_ui/avatar/avatar_image.rb +4 -0
- data/lib/ruby_ui/base.rb +6 -0
- data/lib/ruby_ui/calendar/calendar.rb +3 -1
- data/lib/ruby_ui/calendar/calendar_controller.js +66 -7
- data/lib/ruby_ui/calendar/calendar_days.rb +20 -0
- data/lib/ruby_ui/calendar/calendar_docs.rb +9 -0
- data/lib/ruby_ui/combobox/combobox_badge.rb +17 -0
- data/lib/ruby_ui/combobox/combobox_badge_trigger.rb +47 -0
- data/lib/ruby_ui/combobox/combobox_clear_button.rb +40 -0
- data/lib/ruby_ui/combobox/combobox_controller.js +4 -2
- data/lib/ruby_ui/combobox/combobox_docs.rb +199 -64
- data/lib/ruby_ui/combobox/combobox_input_trigger.rb +64 -0
- data/lib/ruby_ui/combobox/combobox_item_indicator.rb +30 -0
- data/lib/ruby_ui/command/command_controller.js +10 -19
- data/lib/ruby_ui/command/command_dialog.rb +4 -1
- data/lib/ruby_ui/command/command_dialog_content.rb +2 -2
- data/lib/ruby_ui/command/command_dialog_controller.js +34 -0
- data/lib/ruby_ui/command/command_dialog_trigger.rb +2 -2
- data/lib/ruby_ui/data_table/data_table.rb +29 -0
- data/lib/ruby_ui/data_table/data_table_bulk_actions.rb +18 -0
- data/lib/ruby_ui/data_table/data_table_column_toggle.rb +62 -0
- data/lib/ruby_ui/data_table/data_table_column_visibility_controller.js +14 -0
- data/lib/ruby_ui/data_table/data_table_controller.js +57 -0
- data/lib/ruby_ui/data_table/data_table_docs.rb +180 -0
- data/lib/ruby_ui/data_table/data_table_expand_toggle.rb +53 -0
- data/lib/ruby_ui/data_table/data_table_form.rb +39 -0
- data/lib/ruby_ui/data_table/data_table_kaminari_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_manual_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_pagination.rb +100 -0
- data/lib/ruby_ui/data_table/data_table_pagination_bar.rb +15 -0
- data/lib/ruby_ui/data_table/data_table_pagy_adapter.rb +17 -0
- data/lib/ruby_ui/data_table/data_table_per_page_select.rb +35 -0
- data/lib/ruby_ui/data_table/data_table_row_checkbox.rb +30 -0
- data/lib/ruby_ui/data_table/data_table_search.rb +57 -0
- data/lib/ruby_ui/data_table/data_table_search_controller.js +62 -0
- data/lib/ruby_ui/data_table/data_table_select_all_checkbox.rb +21 -0
- data/lib/ruby_ui/data_table/data_table_selection_summary.rb +25 -0
- data/lib/ruby_ui/data_table/data_table_sort_head.rb +112 -0
- data/lib/ruby_ui/data_table/data_table_toolbar.rb +15 -0
- data/lib/ruby_ui/date_picker/date_picker.rb +85 -0
- data/lib/ruby_ui/date_picker/date_picker_docs.rb +23 -0
- data/lib/ruby_ui/native_select/native_select.rb +39 -0
- data/lib/ruby_ui/native_select/native_select_docs.rb +83 -0
- data/lib/ruby_ui/native_select/native_select_group.rb +15 -0
- data/lib/ruby_ui/native_select/native_select_icon.rb +39 -0
- data/lib/ruby_ui/native_select/native_select_option.rb +15 -0
- data/lib/ruby_ui/select/select_value.rb +2 -1
- data/lib/ruby_ui/sheet/sheet.rb +9 -1
- data/lib/ruby_ui/sheet/sheet_controller.js +6 -0
- data/lib/ruby_ui/theme_toggle/theme_toggle.rb +14 -2
- data/lib/ruby_ui/theme_toggle/theme_toggle_controller.js +27 -19
- data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +12 -42
- data/lib/ruby_ui/toast/toast.rb +18 -0
- data/lib/ruby_ui/toast/toast_action.rb +27 -0
- data/lib/ruby_ui/toast/toast_cancel.rb +27 -0
- data/lib/ruby_ui/toast/toast_close.rb +40 -0
- data/lib/ruby_ui/toast/toast_controller.js +151 -0
- data/lib/ruby_ui/toast/toast_description.rb +18 -0
- data/lib/ruby_ui/toast/toast_docs.rb +12 -0
- data/lib/ruby_ui/toast/toast_icon.rb +65 -0
- data/lib/ruby_ui/toast/toast_item.rb +72 -0
- data/lib/ruby_ui/toast/toast_region.rb +124 -0
- data/lib/ruby_ui/toast/toast_title.rb +18 -0
- data/lib/ruby_ui/toast/toaster_controller.js +306 -0
- data/lib/ruby_ui/toggle/toggle.rb +101 -0
- data/lib/ruby_ui/toggle/toggle_controller.js +33 -0
- data/lib/ruby_ui/toggle_group/toggle_group.rb +119 -0
- data/lib/ruby_ui/toggle_group/toggle_group_controller.js +126 -0
- data/lib/ruby_ui/toggle_group/toggle_group_item.rb +67 -0
- data/lib/ruby_ui/tooltip/tooltip_content.rb +12 -5
- data/lib/ruby_ui/tooltip/tooltip_controller.js +58 -22
- data/lib/ruby_ui/tooltip/tooltip_docs.rb +13 -0
- data/lib/ruby_ui/tooltip/tooltip_trigger.rb +10 -3
- data/lib/ruby_ui.rb +3 -1
- metadata +66 -10
- data/lib/ruby_ui/theme_toggle/set_dark_mode.rb +0 -16
- data/lib/ruby_ui/theme_toggle/set_light_mode.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41271e83392afeb5499d950f64ea1e5513c3694fac4521c7599f3ac1c281c979
|
|
4
|
+
data.tar.gz: 5370ed6872556cf2848763b09d6edddecc2b5b822357c3af35e6bbfc1eb07ae0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3694f2acd18f037160228f955f019c20696543b5047f87cb1a3108a272982b69c188bbbec174063add1881c29d3625f0fde34d7fe8d9dfebe9bb29621986831
|
|
7
|
+
data.tar.gz: f0853192ca6871211535ac8e60e1db1dfae5adcba4fe60e4284f147bfe6296ce0ded60436764ff3829db5d20c874f3efcd7f68a56f47f411900f8c95f8d6dac3
|
|
@@ -9,6 +9,7 @@ module RubyUI
|
|
|
9
9
|
source_root File.expand_path("../../ruby_ui", __dir__)
|
|
10
10
|
argument :component_name, type: :string, required: true
|
|
11
11
|
class_option :force, type: :boolean, default: false
|
|
12
|
+
class_option :with_docs, type: :boolean, default: false
|
|
12
13
|
|
|
13
14
|
def generate_component
|
|
14
15
|
if component_not_found?
|
|
@@ -63,7 +64,10 @@ module RubyUI
|
|
|
63
64
|
|
|
64
65
|
def component_folder_path = File.join(self.class.source_root, component_folder_name)
|
|
65
66
|
|
|
66
|
-
def components_file_paths
|
|
67
|
+
def components_file_paths
|
|
68
|
+
files = Dir.glob(File.join(component_folder_path, "*.rb"))
|
|
69
|
+
options["with_docs"] ? files : files.reject { |f| f.end_with?("_docs.rb") }
|
|
70
|
+
end
|
|
67
71
|
|
|
68
72
|
def js_controller_file_paths = Dir.glob(File.join(component_folder_path, "*.js"))
|
|
69
73
|
|
|
@@ -42,6 +42,22 @@ context_menu:
|
|
|
42
42
|
js_packages:
|
|
43
43
|
- "tippy.js"
|
|
44
44
|
|
|
45
|
+
date_picker:
|
|
46
|
+
components:
|
|
47
|
+
- "Input"
|
|
48
|
+
- "Popover"
|
|
49
|
+
- "Calendar"
|
|
50
|
+
|
|
51
|
+
data_table:
|
|
52
|
+
components:
|
|
53
|
+
- "Button"
|
|
54
|
+
- "Checkbox"
|
|
55
|
+
- "DropdownMenu"
|
|
56
|
+
- "Input"
|
|
57
|
+
- "NativeSelect"
|
|
58
|
+
- "Pagination"
|
|
59
|
+
- "Table"
|
|
60
|
+
|
|
45
61
|
dropdown_menu:
|
|
46
62
|
js_packages:
|
|
47
63
|
- "@floating-ui/dom"
|
|
@@ -69,6 +85,22 @@ select:
|
|
|
69
85
|
js_packages:
|
|
70
86
|
- "@floating-ui/dom"
|
|
71
87
|
|
|
88
|
+
toast:
|
|
89
|
+
js_packages: []
|
|
90
|
+
|
|
72
91
|
tooltip:
|
|
92
|
+
components:
|
|
93
|
+
- "Typography"
|
|
94
|
+
|
|
73
95
|
js_packages:
|
|
74
96
|
- "@floating-ui/dom"
|
|
97
|
+
|
|
98
|
+
toggle: {}
|
|
99
|
+
|
|
100
|
+
toggle_group:
|
|
101
|
+
components:
|
|
102
|
+
- "Toggle"
|
|
103
|
+
|
|
104
|
+
theme_toggle:
|
|
105
|
+
components:
|
|
106
|
+
- "Toggle"
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
module RubyUI
|
|
2
2
|
module Generators
|
|
3
3
|
module JavascriptUtils
|
|
4
|
+
TW_ANIMATE_CSS_VERSION = "1.4.0"
|
|
5
|
+
|
|
4
6
|
def install_js_package(package)
|
|
5
7
|
if using_importmap?
|
|
6
8
|
pin_with_importmap(package)
|
|
9
|
+
elsif using_bun?
|
|
10
|
+
run "bun add #{package}"
|
|
7
11
|
elsif using_yarn?
|
|
8
12
|
run "yarn add #{package}"
|
|
9
13
|
elsif using_npm?
|
|
@@ -19,6 +23,8 @@ module RubyUI
|
|
|
19
23
|
case package
|
|
20
24
|
when "motion"
|
|
21
25
|
pin_motion
|
|
26
|
+
when "tw-animate-css"
|
|
27
|
+
pin_tw_animate_css
|
|
22
28
|
when "tippy.js"
|
|
23
29
|
pin_tippy_js
|
|
24
30
|
else
|
|
@@ -27,21 +33,34 @@ module RubyUI
|
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def using_importmap?
|
|
30
|
-
File.exist?(
|
|
36
|
+
File.exist?(rails_root.join("config/importmap.rb")) && File.exist?(rails_root.join("bin/importmap"))
|
|
31
37
|
end
|
|
32
38
|
|
|
33
|
-
def
|
|
39
|
+
def using_bun? = File.exist?(rails_root.join("bun.lock"))
|
|
40
|
+
|
|
41
|
+
def using_npm? = File.exist?(rails_root.join("package-lock.json"))
|
|
42
|
+
|
|
43
|
+
def using_pnpm? = File.exist?(rails_root.join("pnpm-lock.yaml"))
|
|
44
|
+
|
|
45
|
+
def using_yarn? = File.exist?(rails_root.join("yarn.lock"))
|
|
34
46
|
|
|
35
|
-
def
|
|
47
|
+
def pin_tw_animate_css
|
|
48
|
+
say <<~TEXT
|
|
49
|
+
WARNING: Installing tw-animate-css as a CSS asset because Importmap cannot pin CSS-only package exports.
|
|
50
|
+
TEXT
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
empty_directory rails_root.join("vendor/javascript")
|
|
53
|
+
# CDN serves "tw-animate.css"; we save as "tw-animate-css.css" to match package name. Do not "correct" the URL.
|
|
54
|
+
get "https://cdn.jsdelivr.net/npm/tw-animate-css@#{TW_ANIMATE_CSS_VERSION}/dist/tw-animate.css",
|
|
55
|
+
rails_root.join("vendor/javascript/tw-animate-css.css")
|
|
56
|
+
end
|
|
38
57
|
|
|
39
58
|
def pin_motion
|
|
40
59
|
say <<~TEXT
|
|
41
60
|
WARNING: Installing motion from CDN because `bin/importmap pin motion` doesn't download the correct file.
|
|
42
61
|
TEXT
|
|
43
62
|
|
|
44
|
-
inject_into_file
|
|
63
|
+
inject_into_file rails_root.join("config/importmap.rb"), <<~RUBY
|
|
45
64
|
pin "motion", to: "https://cdn.jsdelivr.net/npm/motion@11.11.17/+esm"\n
|
|
46
65
|
RUBY
|
|
47
66
|
end
|
|
@@ -51,11 +70,13 @@ module RubyUI
|
|
|
51
70
|
WARNING: Installing tippy.js from CDN because `bin/importmap pin tippy.js` doesn't download the correct file.
|
|
52
71
|
TEXT
|
|
53
72
|
|
|
54
|
-
inject_into_file
|
|
73
|
+
inject_into_file rails_root.join("config/importmap.rb"), <<~RUBY
|
|
55
74
|
pin "tippy.js", to: "https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/+esm"
|
|
56
75
|
pin "@popperjs/core", to: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/+esm"\n
|
|
57
76
|
RUBY
|
|
58
77
|
end
|
|
78
|
+
|
|
79
|
+
def rails_root = Rails.root
|
|
59
80
|
end
|
|
60
81
|
end
|
|
61
82
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["image", "fallback"];
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
if (!this.hasImageTarget) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (this.imageTarget.complete && this.imageTarget.naturalWidth > 0) {
|
|
12
|
+
this.showImage();
|
|
13
|
+
} else {
|
|
14
|
+
// Image not yet loaded (or failed): hide it so the fallback shows.
|
|
15
|
+
// Image visibility is restored by the load/error handlers.
|
|
16
|
+
this.showFallback();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
showImage() {
|
|
21
|
+
this.imageTargets.forEach((image) => image.classList.remove("hidden"));
|
|
22
|
+
this.fallbackTargets.forEach((fallback) =>
|
|
23
|
+
fallback.classList.add("hidden"),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
showFallback() {
|
|
28
|
+
this.imageTargets.forEach((image) => image.classList.add("hidden"));
|
|
29
|
+
this.fallbackTargets.forEach((fallback) =>
|
|
30
|
+
fallback.classList.remove("hidden"),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -17,6 +17,10 @@ module RubyUI
|
|
|
17
17
|
def default_attrs
|
|
18
18
|
{
|
|
19
19
|
loading: "lazy",
|
|
20
|
+
data: {
|
|
21
|
+
ruby_ui__avatar_target: "image",
|
|
22
|
+
action: "load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback"
|
|
23
|
+
},
|
|
20
24
|
class: "aspect-square h-full w-full",
|
|
21
25
|
alt: @alt,
|
|
22
26
|
src: @src
|
data/lib/ruby_ui/base.rb
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class Calendar < Base
|
|
5
|
-
def initialize(selected_date: nil, input_id: nil, date_format: "yyyy-MM-dd", **attrs)
|
|
5
|
+
def initialize(selected_date: nil, min_date: nil, input_id: nil, date_format: "yyyy-MM-dd", **attrs)
|
|
6
6
|
@selected_date = selected_date
|
|
7
|
+
@min_date = min_date
|
|
7
8
|
@input_id = input_id
|
|
8
9
|
@date_format = date_format
|
|
9
10
|
super(**attrs)
|
|
@@ -30,6 +31,7 @@ module RubyUI
|
|
|
30
31
|
data: {
|
|
31
32
|
controller: "ruby-ui--calendar",
|
|
32
33
|
ruby_ui__calendar_selected_date_value: @selected_date&.to_s,
|
|
34
|
+
ruby_ui__calendar_min_date_value: @min_date&.to_s,
|
|
33
35
|
ruby_ui__calendar_format_value: @date_format,
|
|
34
36
|
ruby_ui__calendar_ruby_ui__calendar_input_outlet: @input_id
|
|
35
37
|
}
|
|
@@ -6,6 +6,7 @@ export default class extends Controller {
|
|
|
6
6
|
"calendar",
|
|
7
7
|
"title",
|
|
8
8
|
"weekdaysTemplate",
|
|
9
|
+
"disabledDateTemplate",
|
|
9
10
|
"selectedDateTemplate",
|
|
10
11
|
"todayDateTemplate",
|
|
11
12
|
"currentMonthDateTemplate",
|
|
@@ -16,6 +17,10 @@ export default class extends Controller {
|
|
|
16
17
|
type: String,
|
|
17
18
|
default: null,
|
|
18
19
|
},
|
|
20
|
+
minDate: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: null,
|
|
23
|
+
},
|
|
19
24
|
viewDate: {
|
|
20
25
|
type: String,
|
|
21
26
|
default: new Date().toISOString().slice(0, 10),
|
|
@@ -43,13 +48,21 @@ export default class extends Controller {
|
|
|
43
48
|
|
|
44
49
|
selectDay(e) {
|
|
45
50
|
e.preventDefault();
|
|
51
|
+
if (this.isDateDisabled(e.currentTarget.dataset.day)) return;
|
|
52
|
+
|
|
46
53
|
// Set the selected date value
|
|
47
54
|
this.selectedDateValue = e.currentTarget.dataset.day;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
selectedDateValueChanged(value, prevValue) {
|
|
58
|
+
const selectedDate = this.selectedDate();
|
|
59
|
+
if (!selectedDate) {
|
|
60
|
+
this.updateCalendar();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
// update the viewDateValue to the first day of month of the selected date (This will trigger updateCalendar() function)
|
|
52
|
-
const newViewDate = new Date(
|
|
65
|
+
const newViewDate = new Date(selectedDate);
|
|
53
66
|
newViewDate.setDate(2); // set the day to the 2nd (to avoid issues with months with different number of days and timezones)
|
|
54
67
|
this.viewDateValue = newViewDate.toISOString().slice(0, 10);
|
|
55
68
|
|
|
@@ -58,7 +71,7 @@ export default class extends Controller {
|
|
|
58
71
|
|
|
59
72
|
// update the input value
|
|
60
73
|
this.rubyUiCalendarInputOutlets.forEach((outlet) => {
|
|
61
|
-
const formattedDate = this.formatDate(
|
|
74
|
+
const formattedDate = this.formatDate(selectedDate);
|
|
62
75
|
outlet.setValue(formattedDate);
|
|
63
76
|
});
|
|
64
77
|
}
|
|
@@ -101,10 +114,20 @@ export default class extends Controller {
|
|
|
101
114
|
|
|
102
115
|
renderDay(day) {
|
|
103
116
|
const today = new Date();
|
|
117
|
+
const selectedDate = this.selectedDate();
|
|
104
118
|
let dateHTML = "";
|
|
105
119
|
const data = { day: day, dayDate: day.getDate() };
|
|
106
120
|
|
|
107
|
-
if (
|
|
121
|
+
if (this.isDateDisabled(day)) {
|
|
122
|
+
// disabledDate
|
|
123
|
+
dateHTML = Mustache.render(
|
|
124
|
+
this.disabledDateTemplateTarget.innerHTML,
|
|
125
|
+
data,
|
|
126
|
+
);
|
|
127
|
+
} else if (
|
|
128
|
+
selectedDate &&
|
|
129
|
+
day.toDateString() === selectedDate.toDateString()
|
|
130
|
+
) {
|
|
108
131
|
// selectedDate
|
|
109
132
|
// Render the selected date template target innerHTML with Mustache
|
|
110
133
|
dateHTML = Mustache.render(
|
|
@@ -137,13 +160,13 @@ export default class extends Controller {
|
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
selectedDate() {
|
|
140
|
-
return
|
|
163
|
+
return this.parseDate(this.selectedDateValue);
|
|
141
164
|
}
|
|
142
165
|
|
|
143
166
|
viewDate() {
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
return (
|
|
168
|
+
this.parseDate(this.viewDateValue) || this.selectedDate() || new Date()
|
|
169
|
+
);
|
|
147
170
|
}
|
|
148
171
|
|
|
149
172
|
getFullWeeksStartAndEndInMonth() {
|
|
@@ -246,4 +269,40 @@ export default class extends Controller {
|
|
|
246
269
|
return "th";
|
|
247
270
|
}
|
|
248
271
|
}
|
|
272
|
+
|
|
273
|
+
minDate() {
|
|
274
|
+
return this.parseDate(this.minDateValue);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
isDateDisabled(date) {
|
|
278
|
+
const minDate = this.minDate();
|
|
279
|
+
const candidate = this.parseDate(date);
|
|
280
|
+
|
|
281
|
+
if (!minDate || !candidate) return false;
|
|
282
|
+
|
|
283
|
+
return this.startOfDay(candidate) < this.startOfDay(minDate);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
parseDate(value) {
|
|
287
|
+
if (!value) return null;
|
|
288
|
+
if (value instanceof Date) return new Date(value);
|
|
289
|
+
|
|
290
|
+
const isoDate = value.toString().match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
291
|
+
if (isoDate) {
|
|
292
|
+
return new Date(
|
|
293
|
+
Number(isoDate[1]),
|
|
294
|
+
Number(isoDate[2]) - 1,
|
|
295
|
+
Number(isoDate[3]),
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const date = new Date(value);
|
|
300
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
startOfDay(date) {
|
|
304
|
+
const normalizedDate = new Date(date);
|
|
305
|
+
normalizedDate.setHours(0, 0, 0, 0);
|
|
306
|
+
return normalizedDate;
|
|
307
|
+
}
|
|
249
308
|
}
|
|
@@ -5,6 +5,7 @@ module RubyUI
|
|
|
5
5
|
BASE_CLASS = "inline-flex items-center justify-center rounded-md text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-8 w-8 p-0 font-normal aria-selected:opacity-100"
|
|
6
6
|
|
|
7
7
|
def view_template
|
|
8
|
+
render_disabled_date_template
|
|
8
9
|
render_selected_date_template
|
|
9
10
|
render_today_date_template
|
|
10
11
|
render_current_month_date_template
|
|
@@ -13,6 +14,25 @@ module RubyUI
|
|
|
13
14
|
|
|
14
15
|
private
|
|
15
16
|
|
|
17
|
+
def render_disabled_date_template
|
|
18
|
+
date_template("disabledDateTemplate") do
|
|
19
|
+
button(
|
|
20
|
+
data_day: "{{day}}",
|
|
21
|
+
name: "day",
|
|
22
|
+
class:
|
|
23
|
+
[
|
|
24
|
+
BASE_CLASS,
|
|
25
|
+
"cursor-not-allowed bg-background text-muted-foreground hover:bg-background hover:text-muted-foreground"
|
|
26
|
+
],
|
|
27
|
+
disabled: true,
|
|
28
|
+
role: "gridcell",
|
|
29
|
+
tabindex: "-1",
|
|
30
|
+
type: "button",
|
|
31
|
+
aria_disabled: "true"
|
|
32
|
+
) { "{{dayDate}}" }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
16
36
|
def render_selected_date_template
|
|
17
37
|
date_template("selectedDateTemplate") do
|
|
18
38
|
button(
|
|
@@ -26,6 +26,15 @@ class Views::Docs::Calendar < Views::Base
|
|
|
26
26
|
RUBY
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
render Docs::VisualCodeExample.new(title: "Minimum date", description: "Disable dates before a given date", context: self) do
|
|
30
|
+
<<~RUBY
|
|
31
|
+
div(class: 'space-y-4') do
|
|
32
|
+
Input(type: 'string', placeholder: "Select a date", class: 'rounded-md border shadow', id: 'minimum-date', data_controller: 'ruby-ui--calendar-input')
|
|
33
|
+
Calendar(input_id: '#minimum-date', min_date: Date.current, class: 'rounded-md border shadow')
|
|
34
|
+
end
|
|
35
|
+
RUBY
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
30
39
|
|
|
31
40
|
render Docs::ComponentsTable.new(component_files(component))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ComboboxBadge < Base
|
|
5
|
+
def view_template(&)
|
|
6
|
+
span(**attrs, &)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def default_attrs
|
|
12
|
+
{
|
|
13
|
+
class: "inline-flex items-center gap-1 rounded-md border bg-secondary px-1.5 py-0.5 text-xs font-medium text-secondary-foreground"
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ComboboxBadgeTrigger < Base
|
|
5
|
+
def initialize(placeholder: "", clear_button: false, **)
|
|
6
|
+
@placeholder = placeholder
|
|
7
|
+
@clear_button = clear_button
|
|
8
|
+
super(**)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&)
|
|
12
|
+
div(**attrs) do
|
|
13
|
+
div(data: {ruby_ui__combobox_target: "badgeContainer"}, class: "hidden")
|
|
14
|
+
input(
|
|
15
|
+
type: "text",
|
|
16
|
+
class: "flex-1 min-w-8 bg-transparent border-0 px-0 outline-none focus:ring-0 placeholder:text-muted-foreground text-sm",
|
|
17
|
+
autocomplete: "off",
|
|
18
|
+
autocorrect: "off",
|
|
19
|
+
spellcheck: "false",
|
|
20
|
+
placeholder: @placeholder,
|
|
21
|
+
data: {
|
|
22
|
+
ruby_ui__combobox_target: "badgeInput",
|
|
23
|
+
action: "keyup->ruby-ui--combobox#filterItems input->ruby-ui--combobox#filterItems keydown.backspace->ruby-ui--combobox#handleBadgeInputBackspace"
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
render ComboboxClearButton.new if @clear_button
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# JS-toggled classes (referenced here so Tailwind compiles them): h-auto min-h-9 pt-1.5
|
|
33
|
+
def default_attrs
|
|
34
|
+
{
|
|
35
|
+
class: "flex h-9 w-full flex-wrap items-center gap-1 rounded-md border border-input bg-background px-3 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 cursor-text",
|
|
36
|
+
data: {
|
|
37
|
+
ruby_ui__combobox_target: "trigger",
|
|
38
|
+
action: "click->ruby-ui--combobox#openPopover focusin->ruby-ui--combobox#openPopover"
|
|
39
|
+
},
|
|
40
|
+
aria: {
|
|
41
|
+
haspopup: "listbox",
|
|
42
|
+
expanded: "false"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ComboboxClearButton < Base
|
|
5
|
+
def view_template
|
|
6
|
+
button(**attrs) do
|
|
7
|
+
svg(
|
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
9
|
+
width: "24",
|
|
10
|
+
height: "24",
|
|
11
|
+
viewbox: "0 0 24 24",
|
|
12
|
+
fill: "none",
|
|
13
|
+
stroke: "currentColor",
|
|
14
|
+
stroke_width: "2",
|
|
15
|
+
stroke_linecap: "round",
|
|
16
|
+
stroke_linejoin: "round",
|
|
17
|
+
class: "size-3.5"
|
|
18
|
+
) do |s|
|
|
19
|
+
s.path(d: "M18 6 6 18")
|
|
20
|
+
s.path(d: "m6 6 12 12")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def default_attrs
|
|
28
|
+
{
|
|
29
|
+
type: "button",
|
|
30
|
+
class: "ml-auto shrink-0 rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring hidden",
|
|
31
|
+
aria: {label: "Clear selection"},
|
|
32
|
+
data: {
|
|
33
|
+
ruby_ui__combobox_target: "clearButton",
|
|
34
|
+
# JS implementation in combobox_controller.js
|
|
35
|
+
action: "ruby-ui--combobox#clearAll"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -4,7 +4,8 @@ import { computePosition, autoUpdate, offset, flip } from "@floating-ui/dom";
|
|
|
4
4
|
// Connects to data-controller="ruby-ui--combobox"
|
|
5
5
|
export default class extends Controller {
|
|
6
6
|
static values = {
|
|
7
|
-
term: String
|
|
7
|
+
term: String,
|
|
8
|
+
minPopoverWidth: { type: Number, default: 240 }
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
static targets = [
|
|
@@ -186,6 +187,7 @@ export default class extends Controller {
|
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
updatePopoverWidth() {
|
|
189
|
-
|
|
190
|
+
const width = Math.max(this.triggerTarget.offsetWidth, this.minPopoverWidthValue)
|
|
191
|
+
this.popoverTarget.style.width = `${width}px`
|
|
190
192
|
}
|
|
191
193
|
}
|