maquina-components 0.3.0 → 0.3.1
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/README.md +26 -0
- data/app/assets/stylesheets/calendar.css +222 -0
- data/app/assets/stylesheets/date_picker.css +172 -0
- data/app/assets/tailwind/maquina_components_engine/engine.css +16 -16
- data/app/helpers/maquina_components/calendar_helper.rb +196 -0
- data/app/helpers/maquina_components/icons_helper.rb +220 -0
- data/app/helpers/maquina_components/table_helper.rb +9 -10
- data/app/javascript/controllers/calendar_controller.js +394 -0
- data/app/javascript/controllers/date_picker_controller.js +261 -0
- data/app/views/components/_calendar.html.erb +121 -0
- data/app/views/components/_date_picker.html.erb +102 -0
- data/app/views/components/calendar/_header.html.erb +22 -0
- data/app/views/components/calendar/_week.html.erb +53 -0
- data/lib/maquina_components/version.rb +1 -1
- metadata +10 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<%# locals: (selected: nil, selected_end: nil, month: nil, year: nil, mode: :single, min_date: nil, max_date: nil, disabled_dates: [], show_outside_days: true, week_starts_on: :sunday, cell_size: nil, input_name: nil, input_name_end: nil, css_classes: "", **html_options) %>
|
|
2
|
+
<%
|
|
3
|
+
# Parse selected dates
|
|
4
|
+
selected_date = case selected
|
|
5
|
+
when String then Date.parse(selected) rescue nil
|
|
6
|
+
when Date, Time, DateTime then selected.to_date
|
|
7
|
+
else nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
selected_end_date = case selected_end
|
|
11
|
+
when String then Date.parse(selected_end) rescue nil
|
|
12
|
+
when Date, Time, DateTime then selected_end.to_date
|
|
13
|
+
else nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Parse min/max dates
|
|
17
|
+
min_date_parsed = case min_date
|
|
18
|
+
when String then Date.parse(min_date) rescue nil
|
|
19
|
+
when Date, Time, DateTime then min_date.to_date
|
|
20
|
+
else nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
max_date_parsed = case max_date
|
|
24
|
+
when String then Date.parse(max_date) rescue nil
|
|
25
|
+
when Date, Time, DateTime then max_date.to_date
|
|
26
|
+
else nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Determine display month/year
|
|
30
|
+
display_date = selected_date || Date.current
|
|
31
|
+
display_month = month || display_date.month
|
|
32
|
+
display_year = year || display_date.year
|
|
33
|
+
|
|
34
|
+
# Build the calendar data
|
|
35
|
+
first_of_month = Date.new(display_year, display_month, 1)
|
|
36
|
+
last_of_month = first_of_month.end_of_month
|
|
37
|
+
|
|
38
|
+
# Calculate start of calendar grid
|
|
39
|
+
week_start = week_starts_on == :monday ? 1 : 0
|
|
40
|
+
days_before = (first_of_month.wday - week_start) % 7
|
|
41
|
+
calendar_start = first_of_month - days_before.days
|
|
42
|
+
|
|
43
|
+
# Calculate end of calendar grid (6 weeks max)
|
|
44
|
+
total_days = days_before + last_of_month.day
|
|
45
|
+
weeks_needed = (total_days / 7.0).ceil
|
|
46
|
+
weeks_needed = [weeks_needed, 6].min
|
|
47
|
+
calendar_end = calendar_start + (weeks_needed * 7 - 1).days
|
|
48
|
+
|
|
49
|
+
# Build weeks array
|
|
50
|
+
weeks = (calendar_start..calendar_end).each_slice(7).to_a
|
|
51
|
+
|
|
52
|
+
# Weekday names
|
|
53
|
+
weekday_names = week_starts_on == :monday ?
|
|
54
|
+
%w[Mo Tu We Th Fr Sa Su] :
|
|
55
|
+
%w[Su Mo Tu We Th Fr Sa]
|
|
56
|
+
|
|
57
|
+
merged_data = (html_options.delete(:data) || {}).merge(
|
|
58
|
+
controller: "calendar",
|
|
59
|
+
component: "calendar",
|
|
60
|
+
"calendar-mode-value": mode,
|
|
61
|
+
"calendar-month-value": display_month,
|
|
62
|
+
"calendar-year-value": display_year,
|
|
63
|
+
"calendar-selected-value": selected_date&.iso8601,
|
|
64
|
+
"calendar-selected-end-value": selected_end_date&.iso8601,
|
|
65
|
+
"calendar-min-date-value": min_date_parsed&.iso8601,
|
|
66
|
+
"calendar-max-date-value": max_date_parsed&.iso8601,
|
|
67
|
+
"calendar-week-starts-on-value": week_starts_on
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
style_attr = cell_size ? "--cell-size: #{cell_size};" : nil
|
|
71
|
+
%>
|
|
72
|
+
|
|
73
|
+
<%= content_tag :div,
|
|
74
|
+
class: css_classes.presence,
|
|
75
|
+
data: merged_data,
|
|
76
|
+
style: style_attr,
|
|
77
|
+
**html_options do %>
|
|
78
|
+
|
|
79
|
+
<%# Hidden inputs for form integration %>
|
|
80
|
+
<% if input_name %>
|
|
81
|
+
<input type="hidden"
|
|
82
|
+
name="<%= input_name %>"
|
|
83
|
+
value="<%= selected_date&.iso8601 %>"
|
|
84
|
+
data-calendar-target="input">
|
|
85
|
+
<% end %>
|
|
86
|
+
<% if input_name_end && mode == :range %>
|
|
87
|
+
<input type="hidden"
|
|
88
|
+
name="<%= input_name_end %>"
|
|
89
|
+
value="<%= selected_end_date&.iso8601 %>"
|
|
90
|
+
data-calendar-target="inputEnd">
|
|
91
|
+
<% end %>
|
|
92
|
+
|
|
93
|
+
<%# Header with navigation %>
|
|
94
|
+
<%= render "components/calendar/header",
|
|
95
|
+
month: display_month,
|
|
96
|
+
year: display_year,
|
|
97
|
+
month_name: I18n.l(first_of_month, format: "%B %Y") %>
|
|
98
|
+
|
|
99
|
+
<%# Weekday headers %>
|
|
100
|
+
<div data-calendar-part="weekdays">
|
|
101
|
+
<% weekday_names.each do |day_name| %>
|
|
102
|
+
<div data-calendar-part="weekday"><%= day_name %></div>
|
|
103
|
+
<% end %>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<%# Calendar grid %>
|
|
107
|
+
<div data-calendar-part="grid" role="grid" aria-label="Calendar">
|
|
108
|
+
<% weeks.each do |week_days| %>
|
|
109
|
+
<%= render "components/calendar/week",
|
|
110
|
+
days: week_days,
|
|
111
|
+
display_month: display_month,
|
|
112
|
+
selected_date: selected_date,
|
|
113
|
+
selected_end_date: selected_end_date,
|
|
114
|
+
mode: mode,
|
|
115
|
+
min_date: min_date_parsed,
|
|
116
|
+
max_date: max_date_parsed,
|
|
117
|
+
disabled_dates: disabled_dates,
|
|
118
|
+
show_outside_days: show_outside_days %>
|
|
119
|
+
<% end %>
|
|
120
|
+
</div>
|
|
121
|
+
<% end %>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<%# locals: (selected: nil, selected_end: nil, mode: :single, min_date: nil, max_date: nil, disabled_dates: [], show_outside_days: true, week_starts_on: :sunday, placeholder: nil, input_name: nil, input_name_end: nil, id: nil, disabled: false, required: false, css_classes: "", **html_options) %>
|
|
2
|
+
<%
|
|
3
|
+
# Generate unique ID for popover targeting
|
|
4
|
+
component_id = id || "date-picker-#{SecureRandom.hex(4)}"
|
|
5
|
+
popover_id = "#{component_id}-popover"
|
|
6
|
+
|
|
7
|
+
# Parse selected dates for display
|
|
8
|
+
selected_date = case selected
|
|
9
|
+
when String then Date.parse(selected) rescue nil
|
|
10
|
+
when Date, Time, DateTime then selected.to_date
|
|
11
|
+
else nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
selected_end_date = case selected_end
|
|
15
|
+
when String then Date.parse(selected_end) rescue nil
|
|
16
|
+
when Date, Time, DateTime then selected_end.to_date
|
|
17
|
+
else nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Format display value
|
|
21
|
+
display_value = if mode == :range && selected_date && selected_end_date
|
|
22
|
+
"#{I18n.l(selected_date, format: :short)} - #{I18n.l(selected_end_date, format: :short)}"
|
|
23
|
+
elsif mode == :range && selected_date
|
|
24
|
+
"#{I18n.l(selected_date, format: :short)} - ..."
|
|
25
|
+
elsif selected_date
|
|
26
|
+
I18n.l(selected_date, format: :long)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
default_placeholder = mode == :range ? "Select date range" : "Select date"
|
|
30
|
+
|
|
31
|
+
merged_data = (html_options.delete(:data) || {}).merge(
|
|
32
|
+
controller: "date-picker",
|
|
33
|
+
component: "date-picker",
|
|
34
|
+
"date-picker-mode-value": mode,
|
|
35
|
+
"date-picker-selected-value": selected_date&.iso8601,
|
|
36
|
+
"date-picker-selected-end-value": selected_end_date&.iso8601
|
|
37
|
+
)
|
|
38
|
+
%>
|
|
39
|
+
|
|
40
|
+
<div id="<%= component_id %>"
|
|
41
|
+
class="<%= css_classes.presence %>"
|
|
42
|
+
data-controller="date-picker"
|
|
43
|
+
data-component="date-picker"
|
|
44
|
+
data-date-picker-mode-value="<%= mode %>"
|
|
45
|
+
data-date-picker-selected-value="<%= selected_date&.iso8601 %>"
|
|
46
|
+
data-date-picker-selected-end-value="<%= selected_end_date&.iso8601 %>">
|
|
47
|
+
|
|
48
|
+
<%# Hidden inputs for form submission %>
|
|
49
|
+
<% if input_name %>
|
|
50
|
+
<input type="hidden"
|
|
51
|
+
name="<%= input_name %>"
|
|
52
|
+
value="<%= selected_date&.iso8601 %>"
|
|
53
|
+
data-date-picker-target="input"
|
|
54
|
+
<%= "required" if required %>>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% if input_name_end && mode == :range %>
|
|
57
|
+
<input type="hidden"
|
|
58
|
+
name="<%= input_name_end %>"
|
|
59
|
+
value="<%= selected_end_date&.iso8601 %>"
|
|
60
|
+
data-date-picker-target="inputEnd">
|
|
61
|
+
<% end %>
|
|
62
|
+
|
|
63
|
+
<%# Trigger button %>
|
|
64
|
+
<button type="button"
|
|
65
|
+
popovertarget="<%= popover_id %>"
|
|
66
|
+
data-date-picker-target="trigger"
|
|
67
|
+
data-date-picker-part="trigger"
|
|
68
|
+
<%= "disabled" if disabled %>
|
|
69
|
+
aria-haspopup="dialog"
|
|
70
|
+
aria-expanded="false">
|
|
71
|
+
<%= icon_for :calendar, class: "size-4" %>
|
|
72
|
+
<span data-date-picker-target="display">
|
|
73
|
+
<%= display_value || placeholder || default_placeholder %>
|
|
74
|
+
</span>
|
|
75
|
+
<% unless display_value %>
|
|
76
|
+
<span data-date-picker-part="placeholder-indicator"></span>
|
|
77
|
+
<% end %>
|
|
78
|
+
</button>
|
|
79
|
+
|
|
80
|
+
<%# Popover with Calendar %>
|
|
81
|
+
<div id="<%= popover_id %>"
|
|
82
|
+
popover
|
|
83
|
+
data-date-picker-target="popover"
|
|
84
|
+
data-date-picker-part="popover"
|
|
85
|
+
role="dialog"
|
|
86
|
+
aria-modal="true"
|
|
87
|
+
aria-label="<%= mode == :range ? 'Date range picker' : 'Date picker' %>">
|
|
88
|
+
<%= render "components/calendar",
|
|
89
|
+
selected: selected_date,
|
|
90
|
+
selected_end: selected_end_date,
|
|
91
|
+
mode: mode,
|
|
92
|
+
min_date: min_date,
|
|
93
|
+
max_date: max_date,
|
|
94
|
+
disabled_dates: disabled_dates,
|
|
95
|
+
show_outside_days: show_outside_days,
|
|
96
|
+
week_starts_on: week_starts_on,
|
|
97
|
+
data: {
|
|
98
|
+
"date-picker-target": "calendar",
|
|
99
|
+
action: "calendar:change->date-picker#handleChange calendar:navigate->date-picker#handleNavigate"
|
|
100
|
+
} %>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%# locals: (month:, year:, month_name:, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge("calendar-part": "header") %>
|
|
3
|
+
|
|
4
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
5
|
+
<button type="button"
|
|
6
|
+
data-action="click->calendar#previousMonth"
|
|
7
|
+
data-calendar-target="prevButton"
|
|
8
|
+
aria-label="Previous month">
|
|
9
|
+
<%= icon_for :chevron_left, class: "size-4" %>
|
|
10
|
+
</button>
|
|
11
|
+
|
|
12
|
+
<div data-calendar-part="caption">
|
|
13
|
+
<%= month_name %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<button type="button"
|
|
17
|
+
data-action="click->calendar#nextMonth"
|
|
18
|
+
data-calendar-target="nextButton"
|
|
19
|
+
aria-label="Next month">
|
|
20
|
+
<%= icon_for :chevron_right, class: "size-4" %>
|
|
21
|
+
</button>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<%# locals: (days:, display_month:, selected_date: nil, selected_end_date: nil, mode: :single, min_date: nil, max_date: nil, disabled_dates: [], show_outside_days: true, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge("calendar-part": "week") %>
|
|
3
|
+
|
|
4
|
+
<%= content_tag :div, role: "row", class: css_classes.presence, data: merged_data, **html_options do %>
|
|
5
|
+
<% days.each do |day| %>
|
|
6
|
+
<%
|
|
7
|
+
is_outside = day.month != display_month
|
|
8
|
+
is_today = day == Date.current
|
|
9
|
+
is_selected = selected_date && day == selected_date
|
|
10
|
+
is_range_end = selected_end_date && day == selected_end_date
|
|
11
|
+
is_range_middle = selected_date && selected_end_date &&
|
|
12
|
+
day > selected_date && day < selected_end_date
|
|
13
|
+
is_disabled = (min_date && day < min_date) ||
|
|
14
|
+
(max_date && day > max_date) ||
|
|
15
|
+
disabled_dates.include?(day)
|
|
16
|
+
|
|
17
|
+
day_state = if is_selected && mode == :range && selected_end_date
|
|
18
|
+
"range-start"
|
|
19
|
+
elsif is_range_end
|
|
20
|
+
"range-end"
|
|
21
|
+
elsif is_range_middle
|
|
22
|
+
"range-middle"
|
|
23
|
+
elsif is_selected
|
|
24
|
+
"selected"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Skip rendering if outside day and not showing them
|
|
28
|
+
next if is_outside && !show_outside_days
|
|
29
|
+
|
|
30
|
+
day_data = {
|
|
31
|
+
"calendar-part": "day",
|
|
32
|
+
"calendar-target": "day",
|
|
33
|
+
date: day.iso8601,
|
|
34
|
+
state: day_state.presence,
|
|
35
|
+
outside: (is_outside ? true : nil),
|
|
36
|
+
today: (is_today ? true : nil),
|
|
37
|
+
action: "click->calendar#selectDay keydown->calendar#handleKeydown"
|
|
38
|
+
}.compact
|
|
39
|
+
%>
|
|
40
|
+
<button type="button"
|
|
41
|
+
data-calendar-part="day"
|
|
42
|
+
data-calendar-target="day"
|
|
43
|
+
data-date="<%= day.iso8601 %>"
|
|
44
|
+
<%= "data-state=\"#{day_state}\"" if day_state %>
|
|
45
|
+
<%= "data-outside" if is_outside %>
|
|
46
|
+
<%= "data-today" if is_today %>
|
|
47
|
+
data-action="click->calendar#selectDay keydown->calendar#handleKeydown"
|
|
48
|
+
<%= "disabled" if is_disabled %>
|
|
49
|
+
tabindex="<%= is_today ? '0' : '-1' %>"
|
|
50
|
+
<%= "aria-selected=\"true\"" if day_state&.include?("selected") || day_state == "range-start" || day_state == "range-end" %>
|
|
51
|
+
<%= "aria-current=\"date\"" if is_today %>><%= day.day %></button>
|
|
52
|
+
<% end %>
|
|
53
|
+
<% end %>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maquina-components
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mario Alberto Chávez
|
|
@@ -51,8 +51,10 @@ files:
|
|
|
51
51
|
- app/assets/stylesheets/alert.css
|
|
52
52
|
- app/assets/stylesheets/badge.css
|
|
53
53
|
- app/assets/stylesheets/breadcrumbs.css
|
|
54
|
+
- app/assets/stylesheets/calendar.css
|
|
54
55
|
- app/assets/stylesheets/card.css
|
|
55
56
|
- app/assets/stylesheets/combobox.css
|
|
57
|
+
- app/assets/stylesheets/date_picker.css
|
|
56
58
|
- app/assets/stylesheets/dropdown_menu.css
|
|
57
59
|
- app/assets/stylesheets/empty.css
|
|
58
60
|
- app/assets/stylesheets/form.css
|
|
@@ -65,6 +67,7 @@ files:
|
|
|
65
67
|
- app/assets/stylesheets/toggle_group.css
|
|
66
68
|
- app/assets/tailwind/maquina_components_engine/engine.css
|
|
67
69
|
- app/helpers/maquina_components/breadcrumbs_helper.rb
|
|
70
|
+
- app/helpers/maquina_components/calendar_helper.rb
|
|
68
71
|
- app/helpers/maquina_components/combobox_helper.rb
|
|
69
72
|
- app/helpers/maquina_components/dropdown_menu_helper.rb
|
|
70
73
|
- app/helpers/maquina_components/empty_helper.rb
|
|
@@ -75,7 +78,9 @@ files:
|
|
|
75
78
|
- app/helpers/maquina_components/toast_helper.rb
|
|
76
79
|
- app/helpers/maquina_components/toggle_group_helper.rb
|
|
77
80
|
- app/javascript/controllers/breadcrumb_controller.js
|
|
81
|
+
- app/javascript/controllers/calendar_controller.js
|
|
78
82
|
- app/javascript/controllers/combobox_controller.js
|
|
83
|
+
- app/javascript/controllers/date_picker_controller.js
|
|
79
84
|
- app/javascript/controllers/dropdown_menu_controller.js
|
|
80
85
|
- app/javascript/controllers/menu_button_controller.js
|
|
81
86
|
- app/javascript/controllers/sidebar_controller.js
|
|
@@ -86,8 +91,10 @@ files:
|
|
|
86
91
|
- app/views/components/_alert.html.erb
|
|
87
92
|
- app/views/components/_badge.html.erb
|
|
88
93
|
- app/views/components/_breadcrumbs.html.erb
|
|
94
|
+
- app/views/components/_calendar.html.erb
|
|
89
95
|
- app/views/components/_card.html.erb
|
|
90
96
|
- app/views/components/_combobox.html.erb
|
|
97
|
+
- app/views/components/_date_picker.html.erb
|
|
91
98
|
- app/views/components/_dropdown.html.erb
|
|
92
99
|
- app/views/components/_dropdown_menu.html.erb
|
|
93
100
|
- app/views/components/_empty.html.erb
|
|
@@ -109,6 +116,8 @@ files:
|
|
|
109
116
|
- app/views/components/breadcrumbs/_list.html.erb
|
|
110
117
|
- app/views/components/breadcrumbs/_page.html.erb
|
|
111
118
|
- app/views/components/breadcrumbs/_separator.html.erb
|
|
119
|
+
- app/views/components/calendar/_header.html.erb
|
|
120
|
+
- app/views/components/calendar/_week.html.erb
|
|
112
121
|
- app/views/components/card/_action.html.erb
|
|
113
122
|
- app/views/components/card/_content.html.erb
|
|
114
123
|
- app/views/components/card/_description.html.erb
|