rails-datepicker 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 315e9fa4e066249f3553a386327bc3412e5cc84f83ad760392b23fe96e957b64
4
- data.tar.gz: ace2e203b08484b807965bec231301a59fa219f0d47f413e2d15f912b7151337
3
+ metadata.gz: 523ca48f16f2917290c2bf4adb263db610a2ecb4c0aaac9682354bdc807a3a9c
4
+ data.tar.gz: 58c1383d56385c1813c93fb130d75e354fd1009820d67dc60dee01183ae47e6d
5
5
  SHA512:
6
- metadata.gz: 489fdedc95b3c29921bffa26ba6e7edb39941b8e5868118ab2e9363254fb6e6aee3bf341e38fff0f5685b8f148dd292629dba13ee88703bce33893f27b5fbbb0
7
- data.tar.gz: 42af843bd420fb1492897b0161c05c13f8664f4dfe2b372d4d2d5c9c74bd60cd8bd431295afd1ccd4720b5554c69d071be4fc1e2329949b34ca4cfbb400b4a58
6
+ metadata.gz: 444a42426714561f3c2b3f077880d3e142d13db34adccf592be862768a659f4388ce081fcb2cb0e6cfd5921fa6870bd21481ef55bd3c905fffbf889129526e12
7
+ data.tar.gz: '089601cf0c5ff55244dfa539d8c1828616175b2cd8b6889cdc1ed9c42ee974734d6b076eadcccfed7a897ea214ef98cdc6a20aecca9b7f348095db5c45ce07a8'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## 0.2.2 - 2026-11-01
6
+
7
+ ### Added
8
+
9
+ - `locale_follows_calendar` field option and configuration setting that shows the Ethiopian calendar in Amharic and the Gregorian calendar in English, hiding the separate language switch.
10
+
11
+ ### Changed
12
+
13
+ - The calendar switch is now a segmented control showing the Ethiopian and British flags with the active calendar highlighted.
14
+
5
15
  ## 0.2.1 - 2026-11-01
6
16
 
7
17
  ### Changed
data/README.md CHANGED
@@ -52,6 +52,7 @@ RailsDatepicker.configure do |config|
52
52
  config.theme = :auto
53
53
  config.week_start = :sunday
54
54
  config.dual_dates = false
55
+ config.locale_follows_calendar = false
55
56
  end
56
57
  ```
57
58
 
@@ -96,6 +97,7 @@ Nested `fields_for` names and IDs are generated by the Rails builder. Standard H
96
97
  theme: :auto,
97
98
  ethiopic_numerals: true,
98
99
  dual_dates: true,
100
+ locale_follows_calendar: true,
99
101
  min: Date.current,
100
102
  max: 2.years.from_now.to_date,
101
103
  disabled_dates: holidays,
@@ -105,7 +107,9 @@ Nested `fields_for` names and IDs are generated by the Rails builder. Standard H
105
107
  years_after: 20 %>
106
108
  ```
107
109
 
108
- `display_calendar: :gregorian` keeps the visible result and manual input Gregorian while the popup can use either calendar. Omit it to display the result in the active calendar. The popup has independent calendar and English/Amharic switches.
110
+ `display_calendar: :gregorian` keeps the visible result and manual input Gregorian while the popup can use either calendar. Omit it to display the result in the active calendar. The popup has a flag-based calendar switch and, unless `locale_follows_calendar` is set, an independent English/Amharic switch.
111
+
112
+ `locale_follows_calendar: true` ties the language to the calendar: the Ethiopian calendar is shown in Amharic and the Gregorian calendar in English. The calendar switch is a segmented control with the Ethiopian and British flags, and the separate language switch is hidden. Any `locale` option is ignored.
109
113
 
110
114
  `dual_dates: true` shows the other calendar's day number beneath each day (for example, the Gregorian day under each Ethiopian day). The first day of the other calendar's month is highlighted, and screen readers hear both dates.
111
115
 
@@ -93,22 +93,22 @@
93
93
  }
94
94
 
95
95
  /* Header */
96
- .rdp__header { display: grid; grid-template-columns: 2rem minmax(0, 1fr) 2rem; gap: .375rem; align-items: center; }
96
+ .rdp__header { display: grid; grid-template-columns: 1.75rem minmax(0, 1fr) 1.75rem; gap: .375rem; align-items: center; }
97
97
  .rdp__nav {
98
- display: grid; width: 2rem; height: 2rem; place-items: center; border-radius: 9999px;
98
+ display: grid; width: 1.75rem; height: 2rem; place-items: center; border-radius: 9999px;
99
99
  color: var(--rdp-muted); font-size: 1.25rem; line-height: 1; padding-bottom: .125rem;
100
100
  transition: background-color .15s ease, color .15s ease;
101
101
  }
102
102
  .rdp__nav:hover:not(:disabled) { background: var(--rdp-hover); color: var(--rdp-accent); }
103
103
  .rdp__nav:active:not(:disabled) { transform: scale(.94); }
104
104
  .rdp__nav:disabled { opacity: .3; }
105
- .rdp__selectors { display: grid; grid-template-columns: minmax(0, 1fr) 5.5rem; gap: .375rem; }
105
+ .rdp__selectors { display: grid; grid-template-columns: minmax(0, 1fr) 4.5rem; gap: .375rem; }
106
106
  .rdp__select {
107
107
  appearance: none; -webkit-appearance: none; min-width: 0; height: 2rem; border: 1px solid var(--rdp-border);
108
- border-radius: .5rem; background: var(--rdp-bg); color: var(--rdp-fg); padding: 0 1.375rem 0 .5rem;
109
- font-size: .8125rem; font-weight: 600; cursor: pointer; text-overflow: ellipsis; white-space: nowrap; overflow: hidden;
108
+ border-radius: .5rem; background: var(--rdp-bg); color: var(--rdp-fg); padding: 0 1.125rem 0 .5rem;
109
+ font-size: .75rem; font-weight: 600; cursor: pointer; text-overflow: ellipsis; white-space: nowrap; overflow: hidden;
110
110
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
111
- background-repeat: no-repeat; background-position: right .5rem center;
111
+ background-repeat: no-repeat; background-position: right .375rem center;
112
112
  transition: border-color .15s ease, background-color .15s ease;
113
113
  }
114
114
  .rdp__select:hover:not(:disabled) { border-color: color-mix(in srgb, var(--rdp-accent) 50%, var(--rdp-border)); background-color: var(--rdp-hover); }
@@ -169,6 +169,16 @@
169
169
  }
170
170
  .rdp__locale-toggle:disabled, .rdp__calendar-toggle:disabled { opacity: .4; }
171
171
  .rdp__locale-toggle { margin-inline-start: auto; }
172
+ .rdp__locale-toggle[hidden] { display: none; }
173
+ .rdp__locale-toggle[hidden] ~ .rdp__calendar-toggle { margin-inline-start: auto; }
174
+ .rdp__calendar-toggle { gap: .125rem; padding: .125rem; }
175
+ .rdp__flag {
176
+ display: inline-grid; place-items: center; width: 1.75rem; height: 1.375rem; border-radius: 9999px;
177
+ opacity: .45; filter: saturate(.5); transition: opacity .15s ease, filter .15s ease, background-color .15s ease;
178
+ }
179
+ .rdp__flag svg { width: 1.25rem; height: .875rem; border-radius: .1875rem; box-shadow: 0 0 0 1px rgb(15 23 42 / .12); }
180
+ .rdp__flag--active { opacity: 1; filter: none; background: var(--rdp-hover); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--rdp-accent) 45%, transparent); }
181
+ .rdp__calendar-toggle:hover:not(:disabled) .rdp__flag:not(.rdp__flag--active) { opacity: .75; }
172
182
 
173
183
  /* Dark theme */
174
184
  .rdp--dark, .dark .rdp--auto {
@@ -21,6 +21,7 @@ export default class extends Controller {
21
21
  weekStart: { type: String, default: "sunday" },
22
22
  ethiopicNumerals: { type: Boolean, default: false },
23
23
  dualDates: { type: Boolean, default: false },
24
+ localeFollowsCalendar: { type: Boolean, default: false },
24
25
  inline: { type: Boolean, default: false },
25
26
  min: String,
26
27
  max: String,
@@ -163,10 +164,13 @@ export default class extends Controller {
163
164
  if (this.inputTarget.disabled) return
164
165
  const anchor = partsToGregorian({ ...calendarParts(this.viewDate, this.calendarValue), day: 1 }, this.calendarValue)
165
166
  this.calendarValue = this.calendarValue === "ethiopian" ? "gregorian" : "ethiopian"
167
+ if (this.localeFollowsCalendarValue) this.localeValue = this.calendarValue === "ethiopian" ? "am" : "en"
166
168
  this.viewDate = anchor
167
169
  if (this.selectedDate) this.inputTarget.value = this.formatSelection(this.selectedDate)
168
170
  this.render()
169
171
  this.announceView()
172
+ if (this.inputTarget.validationMessage) this.validate()
173
+ this.reposition()
170
174
  }
171
175
 
172
176
  switchLocale(event) {
@@ -295,6 +299,7 @@ export default class extends Controller {
295
299
  element.textContent = copy[element.dataset.railsDatepickerText]
296
300
  }
297
301
  if (this.hasLocaleToggleTarget) {
302
+ this.localeToggleTarget.hidden = this.localeFollowsCalendarValue
298
303
  this.localeToggleTarget.textContent = this.localeValue === "am" ? "English" : "አማርኛ"
299
304
  this.localeToggleTarget.setAttribute("aria-label", copy.switchLanguage)
300
305
  }
@@ -303,8 +308,12 @@ export default class extends Controller {
303
308
  renderToggle() {
304
309
  const next = this.calendarValue === "ethiopian" ? "gregorian" : "ethiopian"
305
310
  const copy = labels(this.localeValue)
306
- this.calendarToggleTarget.textContent = copy[next]
307
- this.calendarToggleTarget.setAttribute("aria-label", copy[`switchTo${next[0].toUpperCase()}${next.slice(1)}`])
311
+ const label = copy[`switchTo${next[0].toUpperCase()}${next.slice(1)}`]
312
+ for (const flag of this.calendarToggleTarget.querySelectorAll("[data-rdp-flag]")) {
313
+ flag.classList.toggle("rdp__flag--active", flag.dataset.rdpFlag === this.calendarValue)
314
+ }
315
+ this.calendarToggleTarget.setAttribute("aria-label", label)
316
+ this.calendarToggleTarget.title = label
308
317
  }
309
318
 
310
319
  renderSelectors() {
@@ -8,4 +8,7 @@ RailsDatepicker.configure do |config|
8
8
  config.week_start = :sunday
9
9
  # Show the other calendar's day number beneath each day in the popup.
10
10
  config.dual_dates = false
11
+ # Tie the language to the calendar: Ethiopian in Amharic, Gregorian in English.
12
+ # The calendar switch shows flags and the separate language switch is hidden.
13
+ config.locale_follows_calendar = false
11
14
  end
@@ -7,7 +7,8 @@ module RailsDatepicker
7
7
  THEMES = %i[auto light dark].freeze
8
8
  WEEK_STARTS = %i[sunday monday].freeze
9
9
 
10
- attr_reader :default_calendar, :default_locale, :date_format, :theme, :week_start, :dual_dates
10
+ attr_reader :default_calendar, :default_locale, :date_format, :theme, :week_start, :dual_dates,
11
+ :locale_follows_calendar
11
12
 
12
13
  def initialize
13
14
  @default_calendar = :ethiopian
@@ -16,12 +17,19 @@ module RailsDatepicker
16
17
  @theme = :auto
17
18
  @week_start = :sunday
18
19
  @dual_dates = false
20
+ @locale_follows_calendar = false
19
21
  end
20
22
 
21
23
  def dual_dates=(value)
22
24
  @dual_dates = !!value
23
25
  end
24
26
 
27
+ # When true the language follows the calendar: Ethiopian uses Amharic and
28
+ # Gregorian uses English, and the separate language switch is hidden.
29
+ def locale_follows_calendar=(value)
30
+ @locale_follows_calendar = !!value
31
+ end
32
+
25
33
  def default_calendar=(value)
26
34
  @default_calendar = validate!(:calendar, value, CALENDARS)
27
35
  end
@@ -4,9 +4,14 @@ require "json"
4
4
 
5
5
  module RailsDatepicker
6
6
  module FormHelper
7
+ FLAGS = {
8
+ ethiopian: '<svg viewBox="0 0 20 14" focusable="false"><rect width="20" height="14" fill="#078930"/><rect y="4.67" width="20" height="4.67" fill="#FCDD09"/><rect y="9.33" width="20" height="4.67" fill="#DA121A"/><circle cx="10" cy="7" r="3.7" fill="#0F47AF"/><path d="M10 4.2l1.65 5.07-4.31-3.14h5.32l-4.31 3.14z" fill="#FCDD09"/></svg>',
9
+ gregorian: '<svg viewBox="0 0 20 14" focusable="false"><rect width="20" height="14" fill="#012169"/><path d="M0 0l20 14M20 0L0 14" stroke="#fff" stroke-width="2.8"/><path d="M0 0l20 14M20 0L0 14" stroke="#C8102E" stroke-width="1"/><path d="M10 0v14M0 7h20" stroke="#fff" stroke-width="4"/><path d="M10 0v14M0 7h20" stroke="#C8102E" stroke-width="2.2"/></svg>'
10
+ }.freeze
11
+
7
12
  FIELD_OPTIONS = %i[
8
13
  calendar display_calendar locale date_format format theme week_start ethiopic_numerals
9
- dual_dates inline min max disabled_dates readonly years_before years_after
14
+ dual_dates locale_follows_calendar inline min max disabled_dates readonly years_before years_after
10
15
  ].freeze
11
16
 
12
17
  def rails_datepicker_field(object_name, method, options = {})
@@ -91,11 +96,15 @@ module RailsDatepicker
91
96
  years_after = Integer(options.delete(:years_after) || 20)
92
97
  raise ArgumentError, "year ranges must be non-negative" if years_before.negative? || years_after.negative?
93
98
 
94
- calendar = options.delete(:calendar) || config.default_calendar
99
+ calendar = picker_enum!(:calendar, options.delete(:calendar) || config.default_calendar, Configuration::CALENDARS)
100
+ locale_follows_calendar = options.key?(:locale_follows_calendar) ? !!options.delete(:locale_follows_calendar) : config.locale_follows_calendar
101
+ requested_locale = options.delete(:locale) || config.default_locale
102
+ locale = locale_follows_calendar ? locale_for_calendar(calendar) : picker_enum!(:locale, requested_locale, Configuration::LOCALES)
95
103
  {
96
- calendar: picker_enum!(:calendar, calendar, Configuration::CALENDARS),
104
+ calendar: calendar,
97
105
  display_calendar: (picker_enum!(:display_calendar, options.delete(:display_calendar), Configuration::CALENDARS) if options[:display_calendar]),
98
- locale: picker_enum!(:locale, options.delete(:locale) || config.default_locale, Configuration::LOCALES),
106
+ locale: locale,
107
+ locale_follows_calendar: locale_follows_calendar,
99
108
  date_format: date_format,
100
109
  theme: picker_enum!(:theme, options.delete(:theme) || config.theme, Configuration::THEMES),
101
110
  week_start: picker_enum!(:week_start, options.delete(:week_start) || config.week_start, Configuration::WEEK_STARTS),
@@ -146,6 +155,7 @@ module RailsDatepicker
146
155
  rails_datepicker_week_start_value: picker[:week_start],
147
156
  rails_datepicker_ethiopic_numerals_value: picker[:ethiopic_numerals],
148
157
  rails_datepicker_dual_dates_value: picker[:dual_dates],
158
+ rails_datepicker_locale_follows_calendar_value: picker[:locale_follows_calendar],
149
159
  rails_datepicker_inline_value: picker[:inline],
150
160
  rails_datepicker_min_value: picker[:min].to_s,
151
161
  rails_datepicker_max_value: picker[:max].to_s,
@@ -187,7 +197,7 @@ module RailsDatepicker
187
197
  @template.content_tag(:div, "", class: "rdp__weekdays", aria: { hidden: "true" }, data: { rails_datepicker_target: "weekdays" }),
188
198
  @template.content_tag(:div, "", class: "rdp__grid", role: "grid", data: { rails_datepicker_target: "grid" }),
189
199
  @template.content_tag(:div, "", class: "rdp__sr-only", role: "status", aria: { live: "polite" }, data: { rails_datepicker_target: "status" }),
190
- calendar_footer(picker[:locale], disabled)
200
+ calendar_footer(picker, disabled)
191
201
  ])
192
202
  end
193
203
  end
@@ -207,17 +217,33 @@ module RailsDatepicker
207
217
  end
208
218
  end
209
219
 
210
- def calendar_footer(locale, disabled)
220
+ def calendar_footer(picker, disabled)
221
+ locale = picker[:locale]
211
222
  @template.content_tag(:div, class: "rdp__footer") do
212
223
  @template.safe_join([
213
224
  @template.button_tag(translate("today", locale), type: "button", class: "rdp__footer-button", disabled: disabled, data: { rails_datepicker_text: "today", rails_datepicker_target: "todayButton", action: "rails-datepicker#today" }),
214
225
  @template.button_tag(translate("clear", locale), type: "button", class: "rdp__footer-button", disabled: disabled, data: { rails_datepicker_text: "clear", action: "rails-datepicker#clear" }),
215
- @template.button_tag("", type: "button", class: "rdp__locale-toggle", disabled: disabled, data: { rails_datepicker_target: "localeToggle", action: "rails-datepicker#switchLocale" }),
216
- @template.button_tag("", type: "button", class: "rdp__calendar-toggle", disabled: disabled, data: { rails_datepicker_target: "calendarToggle", action: "rails-datepicker#switchCalendar" })
226
+ @template.button_tag("", type: "button", class: "rdp__locale-toggle", disabled: disabled, hidden: picker[:locale_follows_calendar],
227
+ data: { rails_datepicker_target: "localeToggle", action: "rails-datepicker#switchLocale" }),
228
+ @template.button_tag(calendar_flags(picker[:calendar]), type: "button", class: "rdp__calendar-toggle", disabled: disabled,
229
+ data: { rails_datepicker_target: "calendarToggle", action: "rails-datepicker#switchCalendar" })
217
230
  ])
218
231
  end
219
232
  end
220
233
 
234
+ # Segmented flag control: Ethiopia for the Ethiopian calendar and the United
235
+ # Kingdom for the Gregorian calendar, with the active calendar highlighted.
236
+ def calendar_flags(active)
237
+ @template.safe_join(RailsDatepicker::Configuration::CALENDARS.map do |calendar|
238
+ classes = ["rdp__flag", ("rdp__flag--active" if calendar == active)].compact.join(" ")
239
+ @template.content_tag(:span, RailsDatepicker::FormHelper::FLAGS.fetch(calendar).html_safe, class: classes, data: { rdp_flag: calendar }, aria: { hidden: "true" })
240
+ end)
241
+ end
242
+
243
+ def locale_for_calendar(calendar)
244
+ calendar == :ethiopian ? :am : :en
245
+ end
246
+
221
247
  def nav_button(label, action, glyph, locale, disabled, target)
222
248
  @template.button_tag(glyph, type: "button", class: "rdp__nav", disabled: disabled, aria: { label: translate(label, locale) }, data: { rails_datepicker_label: label, rails_datepicker_target: target, action: "rails-datepicker##{action}" })
223
249
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsDatepicker
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rails-datepicker",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-datepicker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tamiru Hailu