playbook_ui 15.6.0 → 15.7.0.pre.alpha.play263313229
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/app/pb_kits/playbook/_playbook.scss +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +3 -2
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +4 -0
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +95 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.html.erb +43 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_colors_rails.md +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.html.erb +11 -5
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_background_control_rails.md +7 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.jsx +54 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background.md +9 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.jsx +80 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_background_multi.md +3 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +4 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +2 -2
- data/app/pb_kits/playbook/pb_advanced_table/table_header.rb +57 -0
- data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +6 -0
- data/app/pb_kits/playbook/pb_card/docs/_card_header.md +1 -1
- data/app/pb_kits/playbook/pb_card/docs/_card_highlight.md +1 -1
- data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.tsx +6 -0
- data/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +2 -2
- data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleIcon.tsx +10 -8
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_icons.jsx +0 -1
- data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_state.jsx +0 -3
- data/app/pb_kits/playbook/pb_contact/_contact.tsx +51 -24
- data/app/pb_kits/playbook/pb_contact/contact.html.erb +53 -19
- data/app/pb_kits/playbook/pb_contact/contact.rb +11 -1
- data/app/pb_kits/playbook/pb_contact/contact.test.js +76 -0
- data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled.html.erb +33 -0
- data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled.jsx +46 -0
- data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled_rails.md +2 -0
- data/app/pb_kits/playbook/pb_contact/docs/_contact_unstyled_react.md +2 -0
- data/app/pb_kits/playbook/pb_contact/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_contact/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +24 -0
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +197 -7
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.html.erb +23 -14
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_range_pattern_rails.md +1 -1
- data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +2 -1
- data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -1
- data/app/pb_kits/playbook/pb_dialog/dialog.rb +1 -0
- data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +14 -0
- data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +5 -4
- data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +2 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.html.erb +24 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.jsx +60 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.md +3 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible.html.erb +71 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible.jsx +57 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible_rails.md +1 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_overflow_visible_react.md +1 -0
- data/app/pb_kits/playbook/pb_dialog/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_dialog/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_distribution_bar/docs/_distribution_bar_custom_colors.md +1 -1
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +316 -15
- data/app/pb_kits/playbook/pb_draggable/context/types.ts +1 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_default_rails.html.erb +7 -5
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_default_dates.html.erb +19 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.html.erb +12 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_rails.md +26 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.html.erb +19 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_range_end_rails.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.html.erb +30 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_default_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.html.erb +29 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_with_date_pickers_rails.md +13 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_custom_display_rails.html.erb +3 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +39 -5
- data/app/pb_kits/playbook/pb_dropdown/index.js +171 -3
- data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +75 -0
- data/app/pb_kits/playbook/pb_filter/Filter/FilterBackground.tsx +3 -3
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +2 -1
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +14 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_gauge/_gauge.tsx +6 -0
- data/app/pb_kits/playbook/pb_line_graph/_line_graph.tsx +6 -0
- data/app/pb_kits/playbook/pb_radio/docs/_radio_error.md +1 -1
- data/app/pb_kits/playbook/pb_select/_select.tsx +8 -3
- data/app/pb_kits/playbook/pb_select/docs/_select_error.md +1 -1
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.html.erb +16 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.jsx +30 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_input_options.md +1 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
- data/app/pb_kits/playbook/pb_select/select.rb +3 -1
- data/app/pb_kits/playbook/pb_select/select.test.js +23 -0
- data/app/pb_kits/playbook/pb_table/_table.tsx +187 -33
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.jsx +134 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant.md +34 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.html.erb +101 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_rails.md +33 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.jsx +180 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination.md +3 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.html.erb +122 -0
- data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_variant_with_pagination_rails.md +3 -0
- data/app/pb_kits/playbook/pb_table/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_table/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_table/table.html.erb +68 -12
- data/app/pb_kits/playbook/pb_table/table.rb +22 -3
- data/app/pb_kits/playbook/pb_table/table.test.js +143 -0
- data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +15 -3
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.md +1 -1
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.html.erb +6 -0
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.jsx +25 -0
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_text_input/docs/example.yml +3 -0
- data/app/pb_kits/playbook/pb_text_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +6 -0
- data/app/pb_kits/playbook/pb_text_input/text_input.rb +2 -0
- data/app/pb_kits/playbook/pb_text_input/text_input.test.js +16 -0
- data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.md +1 -1
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +296 -0
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +822 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.html.erb +2 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.jsx +16 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_24_hour.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.html.erb +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.jsx +13 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.html.erb +4 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.jsx +29 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_default_time.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.html.erb +13 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_disabled.jsx +23 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.html.erb +5 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_error.jsx +15 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_input_options.html.erb +14 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.html.erb +2 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_label.jsx +15 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.html.erb +42 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.jsx +52 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_min_max_time.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.jsx +45 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_on_handler.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.html.erb +3 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.jsx +21 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/_time_picker_timezone.md +1 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/example.yml +24 -0
- data/app/pb_kits/playbook/pb_time_picker/docs/index.js +9 -0
- data/app/pb_kits/playbook/pb_time_picker/index.ts +40 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.html.erb +1 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +80 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +114 -0
- data/app/pb_kits/playbook/pb_time_picker/time_picker_helper.ts +662 -0
- data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.html.erb +60 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.jsx +118 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.md +1 -0
- data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_date.md +1 -1
- data/app/pb_kits/playbook/pb_timeline/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_timeline/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_timeline/item.html.erb +1 -1
- data/app/pb_kits/playbook/pb_timeline/item.rb +2 -0
- data/app/pb_kits/playbook/pb_timeline/label.html.erb +2 -1
- data/app/pb_kits/playbook/pb_timeline/label.rb +2 -0
- data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +3 -0
- data/app/pb_kits/playbook/pb_timeline/timeline.test.js +51 -0
- data/app/pb_kits/playbook/tokens/_colors.scss +2 -1
- data/app/pb_kits/playbook/utilities/deprecated.ts +73 -0
- data/app/pb_kits/playbook/utilities/globalProps.ts +1 -0
- data/dist/chunks/_typeahead-Ckz1ce-2.js +6 -0
- data/dist/chunks/lib-DxDBrGZX.js +29 -0
- data/dist/chunks/vendor.js +3 -3
- data/dist/menu.yml +16 -9
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/collection_select_field.rb +9 -1
- data/lib/playbook/forms/builder/form_field_builder.rb +13 -2
- data/lib/playbook/forms/builder/select_field.rb +9 -1
- data/lib/playbook/forms/builder/time_picker_field.rb +24 -0
- data/lib/playbook/forms/builder/time_zone_select_field.rb +9 -1
- data/lib/playbook/forms/builder.rb +1 -0
- data/lib/playbook/pb_doc_helper.rb +3 -0
- data/lib/playbook/pb_kit_helper.rb +35 -0
- data/lib/playbook/version.rb +2 -2
- metadata +85 -4
- data/dist/chunks/_typeahead-DecTL7bt.js +0 -6
- data/dist/chunks/lib-Dk4GKPut.js +0 -29
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
// Time Picker Helper Functions
|
|
2
|
+
// Utility functions for parsing, validating, and formatting time values
|
|
3
|
+
|
|
4
|
+
export type ParsedTime = {
|
|
5
|
+
hour: number
|
|
6
|
+
minute: number
|
|
7
|
+
meridiem: 'AM' | 'PM'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type TimeFormat = 'AMPM' | '24hour'
|
|
11
|
+
|
|
12
|
+
export type HourConstraints = {
|
|
13
|
+
maxHour: number
|
|
14
|
+
minHour: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type HourInputResult = {
|
|
18
|
+
value: string
|
|
19
|
+
hour: number | null
|
|
20
|
+
isValid: boolean
|
|
21
|
+
capped: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type MinuteInputResult = {
|
|
25
|
+
value: string
|
|
26
|
+
minute: number | null
|
|
27
|
+
isValid: boolean
|
|
28
|
+
capped: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type FormatConversionResult = {
|
|
32
|
+
hour: number
|
|
33
|
+
meridiem: 'AM' | 'PM'
|
|
34
|
+
changed: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ===========================================================
|
|
38
|
+
// | Time Parsing Utilities |
|
|
39
|
+
// ===========================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse a time string to minutes since midnight for comparison
|
|
43
|
+
* @param timeStr - Time string in "HH:MM" format (24-hour)
|
|
44
|
+
* @returns Minutes since midnight, or null if invalid
|
|
45
|
+
*/
|
|
46
|
+
export const parseTimeToMinutes = (timeStr: string | undefined): number | null => {
|
|
47
|
+
if (!timeStr) return null
|
|
48
|
+
const match = timeStr.match(/(\d{1,2}):(\d{2})/)
|
|
49
|
+
if (match) {
|
|
50
|
+
const h = parseInt(match[1], 10)
|
|
51
|
+
const m = parseInt(match[2], 10)
|
|
52
|
+
return h * 60 + m
|
|
53
|
+
}
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse a time string into hour, minute, and meridiem components
|
|
59
|
+
* @param timeStr - Time string in "HH:MM" (24-hour) or "H:MM AM/PM" (12-hour) format
|
|
60
|
+
* @param timeFormat - The target time format ('AMPM' or '24hour')
|
|
61
|
+
* @returns Parsed time object with hour, minute, and meridiem
|
|
62
|
+
*/
|
|
63
|
+
export const parseTime = (
|
|
64
|
+
timeStr: string | undefined,
|
|
65
|
+
timeFormat: TimeFormat = 'AMPM'
|
|
66
|
+
): ParsedTime => {
|
|
67
|
+
if (!timeStr) {
|
|
68
|
+
// Default to 12:00 PM when no time is provided
|
|
69
|
+
return {
|
|
70
|
+
hour: 12,
|
|
71
|
+
minute: 0,
|
|
72
|
+
meridiem: 'PM',
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)?/i)
|
|
77
|
+
if (match) {
|
|
78
|
+
let hour = parseInt(match[1], 10)
|
|
79
|
+
const minute = parseInt(match[2], 10)
|
|
80
|
+
let meridiem = match[3]?.toUpperCase() as 'AM' | 'PM'
|
|
81
|
+
|
|
82
|
+
if (timeFormat === '24hour') {
|
|
83
|
+
// For 24-hour format, store hour as-is (0-23) but keep meridiem for internal state
|
|
84
|
+
// If meridiem is provided, convert to 24-hour first
|
|
85
|
+
if (meridiem) {
|
|
86
|
+
if (meridiem === 'PM' && hour !== 12) hour += 12
|
|
87
|
+
if (meridiem === 'AM' && hour === 12) hour = 0
|
|
88
|
+
}
|
|
89
|
+
if (hour > 23) hour = 23
|
|
90
|
+
if (hour < 0) hour = 0
|
|
91
|
+
meridiem = hour < 12 ? 'AM' : 'PM'
|
|
92
|
+
return { hour, minute, meridiem }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 12-hour format (AMPM)
|
|
96
|
+
// If no meridiem specified, infer from hour (assuming 24-hour format input)
|
|
97
|
+
if (!meridiem) {
|
|
98
|
+
// Input is likely 24-hour format, convert to 12-hour
|
|
99
|
+
if (hour > 12) {
|
|
100
|
+
hour = hour % 12
|
|
101
|
+
meridiem = 'PM'
|
|
102
|
+
} else if (hour === 12) {
|
|
103
|
+
meridiem = 'PM'
|
|
104
|
+
} else if (hour === 0) {
|
|
105
|
+
hour = 12
|
|
106
|
+
meridiem = 'AM'
|
|
107
|
+
} else {
|
|
108
|
+
meridiem = 'AM'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { hour, minute, meridiem }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { hour: 12, minute: 0, meridiem: 'PM' }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ===========================================================
|
|
119
|
+
// | Time Validation Utilities |
|
|
120
|
+
// ===========================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a given time is within the allowed min/max range
|
|
124
|
+
* @param h - Hour value
|
|
125
|
+
* @param m - Minute value
|
|
126
|
+
* @param mer - Meridiem ('AM' or 'PM'), required for AMPM format
|
|
127
|
+
* @param timeFormat - The time format being used
|
|
128
|
+
* @param minTimeMinutes - Minimum time in minutes since midnight
|
|
129
|
+
* @param maxTimeMinutes - Maximum time in minutes since midnight
|
|
130
|
+
* @returns True if time is within range
|
|
131
|
+
*/
|
|
132
|
+
export const isTimeInRange = (
|
|
133
|
+
h: number,
|
|
134
|
+
m: number,
|
|
135
|
+
mer: 'AM' | 'PM' | undefined,
|
|
136
|
+
timeFormat: TimeFormat,
|
|
137
|
+
minTimeMinutes: number | null,
|
|
138
|
+
maxTimeMinutes: number | null
|
|
139
|
+
): boolean => {
|
|
140
|
+
let hour24 = h
|
|
141
|
+
// Convert to 24-hour if we have meridiem and are in AMPM mode
|
|
142
|
+
if (timeFormat === 'AMPM' && mer) {
|
|
143
|
+
if (mer === 'PM' && h !== 12) hour24 = h + 12
|
|
144
|
+
if (mer === 'AM' && h === 12) hour24 = 0
|
|
145
|
+
}
|
|
146
|
+
const timeInMinutes = hour24 * 60 + m
|
|
147
|
+
|
|
148
|
+
if (minTimeMinutes !== null && timeInMinutes < minTimeMinutes) return false
|
|
149
|
+
if (maxTimeMinutes !== null && timeInMinutes > maxTimeMinutes) return false
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if a specific hour is completely disabled (all minutes are out of range)
|
|
155
|
+
* @param h - Hour value
|
|
156
|
+
* @param mer - Meridiem ('AM' or 'PM')
|
|
157
|
+
* @param timeFormat - The time format being used
|
|
158
|
+
* @param minTimeMinutes - Minimum time in minutes since midnight
|
|
159
|
+
* @param maxTimeMinutes - Maximum time in minutes since midnight
|
|
160
|
+
* @returns True if the entire hour is disabled
|
|
161
|
+
*/
|
|
162
|
+
export const isHourDisabled = (
|
|
163
|
+
h: number,
|
|
164
|
+
mer: 'AM' | 'PM' | undefined,
|
|
165
|
+
timeFormat: TimeFormat,
|
|
166
|
+
minTimeMinutes: number | null,
|
|
167
|
+
maxTimeMinutes: number | null
|
|
168
|
+
): boolean => {
|
|
169
|
+
// Check if any minute in this hour is valid
|
|
170
|
+
for (let m = 0; m < 60; m++) {
|
|
171
|
+
if (isTimeInRange(h, m, mer, timeFormat, minTimeMinutes, maxTimeMinutes)) return false
|
|
172
|
+
}
|
|
173
|
+
return true
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if ANY AM time is valid in the range (for disabling AM toggle)
|
|
178
|
+
* @param minTimeMinutes - Minimum time in minutes since midnight
|
|
179
|
+
* @param maxTimeMinutes - Maximum time in minutes since midnight
|
|
180
|
+
* @returns True if any AM time is valid
|
|
181
|
+
*/
|
|
182
|
+
export const isAnyAMTimeValid = (
|
|
183
|
+
minTimeMinutes: number | null,
|
|
184
|
+
maxTimeMinutes: number | null
|
|
185
|
+
): boolean => {
|
|
186
|
+
if (minTimeMinutes === null && maxTimeMinutes === null) return true
|
|
187
|
+
// AM times are 00:00 to 11:59 (0-719 minutes)
|
|
188
|
+
const amStart = 0
|
|
189
|
+
const amEnd = 719
|
|
190
|
+
const rangeStart = minTimeMinutes ?? 0
|
|
191
|
+
const rangeEnd = maxTimeMinutes ?? 1439
|
|
192
|
+
// Check if AM range overlaps with valid range
|
|
193
|
+
return amStart <= rangeEnd && amEnd >= rangeStart
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Check if ANY PM time is valid in the range (for disabling PM toggle)
|
|
198
|
+
* @param minTimeMinutes - Minimum time in minutes since midnight
|
|
199
|
+
* @param maxTimeMinutes - Maximum time in minutes since midnight
|
|
200
|
+
* @returns True if any PM time is valid
|
|
201
|
+
*/
|
|
202
|
+
export const isAnyPMTimeValid = (
|
|
203
|
+
minTimeMinutes: number | null,
|
|
204
|
+
maxTimeMinutes: number | null
|
|
205
|
+
): boolean => {
|
|
206
|
+
if (minTimeMinutes === null && maxTimeMinutes === null) return true
|
|
207
|
+
// PM times are 12:00 to 23:59 (720-1439 minutes)
|
|
208
|
+
const pmStart = 720
|
|
209
|
+
const pmEnd = 1439
|
|
210
|
+
const rangeStart = minTimeMinutes ?? 0
|
|
211
|
+
const rangeEnd = maxTimeMinutes ?? 1439
|
|
212
|
+
// Check if PM range overlaps with valid range
|
|
213
|
+
return pmStart <= rangeEnd && pmEnd >= rangeStart
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ===========================================================
|
|
217
|
+
// | Time Formatting Utilities |
|
|
218
|
+
// ===========================================================
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Format time for display in the input field
|
|
222
|
+
* @param h - Hour value
|
|
223
|
+
* @param m - Minute value
|
|
224
|
+
* @param mer - Meridiem ('AM' or 'PM')
|
|
225
|
+
* @param timeFormat - The time format to use for display
|
|
226
|
+
* @returns Formatted time string
|
|
227
|
+
*/
|
|
228
|
+
export const getDisplayTime = (
|
|
229
|
+
h: number,
|
|
230
|
+
m: number,
|
|
231
|
+
mer: 'AM' | 'PM',
|
|
232
|
+
timeFormat: TimeFormat
|
|
233
|
+
): string => {
|
|
234
|
+
if (timeFormat === '24hour') {
|
|
235
|
+
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
|
236
|
+
}
|
|
237
|
+
return `${h}:${m.toString().padStart(2, '0')} ${mer}`
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Convert time to 24-hour format string for onChange/onClose callbacks
|
|
242
|
+
* @param h - Hour value
|
|
243
|
+
* @param m - Minute value
|
|
244
|
+
* @param mer - Meridiem ('AM' or 'PM')
|
|
245
|
+
* @param timeFormat - The current time format
|
|
246
|
+
* @returns Time string in "HH:MM" 24-hour format
|
|
247
|
+
*/
|
|
248
|
+
export const get24HourTime = (
|
|
249
|
+
h: number,
|
|
250
|
+
m: number,
|
|
251
|
+
mer: 'AM' | 'PM',
|
|
252
|
+
timeFormat: TimeFormat
|
|
253
|
+
): string => {
|
|
254
|
+
if (timeFormat === '24hour') {
|
|
255
|
+
// Already in 24-hour format
|
|
256
|
+
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
|
257
|
+
}
|
|
258
|
+
// Convert from 12-hour to 24-hour
|
|
259
|
+
let hour24 = h
|
|
260
|
+
if (mer === 'PM' && h !== 12) hour24 = h + 12
|
|
261
|
+
if (mer === 'AM' && h === 12) hour24 = 0
|
|
262
|
+
return `${hour24.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Format a 24-hour time string to display format based on timeFormat
|
|
267
|
+
* @param time24 - Time string in "HH:MM" 24-hour format
|
|
268
|
+
* @param timeFormat - The target time format for display
|
|
269
|
+
* @returns Formatted time string
|
|
270
|
+
*/
|
|
271
|
+
export const formatTimeForDisplay = (
|
|
272
|
+
time24: string,
|
|
273
|
+
timeFormat: TimeFormat
|
|
274
|
+
): string => {
|
|
275
|
+
const match = time24.match(/(\d{1,2}):(\d{2})/)
|
|
276
|
+
if (!match) return time24
|
|
277
|
+
|
|
278
|
+
let h = parseInt(match[1], 10)
|
|
279
|
+
const m = parseInt(match[2], 10)
|
|
280
|
+
const mStr = m.toString().padStart(2, '0')
|
|
281
|
+
|
|
282
|
+
if (timeFormat === '24hour') {
|
|
283
|
+
return `${h}:${mStr}`
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 12-hour format
|
|
287
|
+
const meridiem = h >= 12 ? 'PM' : 'AM'
|
|
288
|
+
if (h === 0) h = 12
|
|
289
|
+
else if (h > 12) h = h - 12
|
|
290
|
+
return `${h}:${mStr}${meridiem}`
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Generate the time range error message in the correct format
|
|
295
|
+
* @param minTime - Minimum time in "HH:MM" format
|
|
296
|
+
* @param maxTime - Maximum time in "HH:MM" format
|
|
297
|
+
* @param timeFormat - The time format for display
|
|
298
|
+
* @returns Error message string
|
|
299
|
+
*/
|
|
300
|
+
export const getTimeRangeErrorMessage = (
|
|
301
|
+
minTime: string | undefined,
|
|
302
|
+
maxTime: string | undefined,
|
|
303
|
+
timeFormat: TimeFormat
|
|
304
|
+
): string => {
|
|
305
|
+
const minFormatted = minTime ? formatTimeForDisplay(minTime, timeFormat) : null
|
|
306
|
+
const maxFormatted = maxTime ? formatTimeForDisplay(maxTime, timeFormat) : null
|
|
307
|
+
|
|
308
|
+
if (minFormatted && maxFormatted) {
|
|
309
|
+
return `Select a time between ${minFormatted} and ${maxFormatted}.`
|
|
310
|
+
} else if (minFormatted) {
|
|
311
|
+
return `Select a time after ${minFormatted}.`
|
|
312
|
+
} else if (maxFormatted) {
|
|
313
|
+
return `Select a time before ${maxFormatted}.`
|
|
314
|
+
}
|
|
315
|
+
return 'Selected time is out of range.'
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ===========================================================
|
|
319
|
+
// | Timezone Utilities |
|
|
320
|
+
// ===========================================================
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get the current timezone text for display
|
|
324
|
+
* @returns Timezone string like "EST (Eastern Standard Time)"
|
|
325
|
+
*/
|
|
326
|
+
export const getTimezoneText = (): string => {
|
|
327
|
+
const date = new Date()
|
|
328
|
+
const tzAbbr = date
|
|
329
|
+
.toLocaleDateString('en-US', {
|
|
330
|
+
day: '2-digit',
|
|
331
|
+
timeZoneName: 'short',
|
|
332
|
+
})
|
|
333
|
+
.slice(4)
|
|
334
|
+
const tzText = date
|
|
335
|
+
.toLocaleDateString('en-US', {
|
|
336
|
+
day: '2-digit',
|
|
337
|
+
timeZoneName: 'long',
|
|
338
|
+
})
|
|
339
|
+
.slice(4)
|
|
340
|
+
return `${tzAbbr} (${tzText})`
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ===========================================================
|
|
344
|
+
// | Hour/Minute Input Handling |
|
|
345
|
+
// ===========================================================
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get the hour constraints based on time format
|
|
349
|
+
* @param timeFormat - The time format being used
|
|
350
|
+
* @returns Object with maxHour and minHour
|
|
351
|
+
*/
|
|
352
|
+
export const getHourConstraints = (timeFormat: TimeFormat): HourConstraints => {
|
|
353
|
+
return {
|
|
354
|
+
maxHour: timeFormat === '24hour' ? 23 : 12,
|
|
355
|
+
minHour: timeFormat === '24hour' ? 0 : 1,
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Validate and process hour input value
|
|
361
|
+
* @param rawValue - The raw input value
|
|
362
|
+
* @param timeFormat - The time format being used
|
|
363
|
+
* @returns Result object with processed value, hour number, and validation status
|
|
364
|
+
*/
|
|
365
|
+
export const processHourInput = (
|
|
366
|
+
rawValue: string,
|
|
367
|
+
timeFormat: TimeFormat
|
|
368
|
+
): HourInputResult => {
|
|
369
|
+
// Allow empty for deletion
|
|
370
|
+
if (rawValue === '') {
|
|
371
|
+
return { value: '', hour: null, isValid: true, capped: false }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Only allow digits
|
|
375
|
+
if (!/^\d+$/.test(rawValue)) {
|
|
376
|
+
return { value: rawValue, hour: null, isValid: false, capped: false }
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Limit to 2 characters max
|
|
380
|
+
if (rawValue.length > 2) {
|
|
381
|
+
return { value: rawValue, hour: null, isValid: false, capped: false }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Parse the numeric value
|
|
385
|
+
const numValue = parseInt(rawValue, 10)
|
|
386
|
+
if (isNaN(numValue)) {
|
|
387
|
+
return { value: rawValue, hour: null, isValid: false, capped: false }
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const { maxHour, minHour } = getHourConstraints(timeFormat)
|
|
391
|
+
|
|
392
|
+
// Cap at max if exceeded
|
|
393
|
+
if (numValue > maxHour) {
|
|
394
|
+
return { value: maxHour.toString(), hour: maxHour, isValid: true, capped: true }
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Return valid result - preserve raw value for display (leading zeros)
|
|
398
|
+
return {
|
|
399
|
+
value: rawValue,
|
|
400
|
+
hour: numValue >= minHour ? numValue : null,
|
|
401
|
+
isValid: true,
|
|
402
|
+
capped: false,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Normalize hour input value on blur
|
|
408
|
+
* @param inputValue - The current input value
|
|
409
|
+
* @param currentHour - The current hour state
|
|
410
|
+
* @param timeFormat - The time format being used
|
|
411
|
+
* @returns Object with normalized hour and display value
|
|
412
|
+
*/
|
|
413
|
+
export const normalizeHourOnBlur = (
|
|
414
|
+
inputValue: string,
|
|
415
|
+
currentHour: number,
|
|
416
|
+
timeFormat: TimeFormat
|
|
417
|
+
): { hour: number; displayValue: string } => {
|
|
418
|
+
if (inputValue === '' || isNaN(parseInt(inputValue, 10))) {
|
|
419
|
+
return { hour: currentHour, displayValue: currentHour.toString() }
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
let numValue = parseInt(inputValue, 10)
|
|
423
|
+
const { maxHour, minHour } = getHourConstraints(timeFormat)
|
|
424
|
+
|
|
425
|
+
if (numValue > maxHour) numValue = maxHour
|
|
426
|
+
if (numValue < minHour) numValue = minHour
|
|
427
|
+
|
|
428
|
+
return { hour: numValue, displayValue: numValue.toString() }
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Validate and process minute input value
|
|
433
|
+
* @param rawValue - The raw input value
|
|
434
|
+
* @returns Result object with processed value, minute number, and validation status
|
|
435
|
+
*/
|
|
436
|
+
export const processMinuteInput = (rawValue: string): MinuteInputResult => {
|
|
437
|
+
// Allow empty for deletion
|
|
438
|
+
if (rawValue === '') {
|
|
439
|
+
return { value: '', minute: null, isValid: true, capped: false }
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Only allow digits
|
|
443
|
+
if (!/^\d+$/.test(rawValue)) {
|
|
444
|
+
return { value: rawValue, minute: null, isValid: false, capped: false }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Limit to 2 characters max
|
|
448
|
+
if (rawValue.length > 2) {
|
|
449
|
+
return { value: rawValue, minute: null, isValid: false, capped: false }
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Parse the numeric value
|
|
453
|
+
const numValue = parseInt(rawValue, 10)
|
|
454
|
+
if (isNaN(numValue)) {
|
|
455
|
+
return { value: rawValue, minute: null, isValid: false, capped: false }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Cap at 59 if exceeded
|
|
459
|
+
if (numValue > 59) {
|
|
460
|
+
return { value: '59', minute: 59, isValid: true, capped: true }
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
value: rawValue,
|
|
465
|
+
minute: numValue >= 0 ? numValue : null,
|
|
466
|
+
isValid: true,
|
|
467
|
+
capped: false,
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Normalize minute input value on blur
|
|
473
|
+
* @param inputValue - The current input value
|
|
474
|
+
* @param currentMinute - The current minute state
|
|
475
|
+
* @returns Object with normalized minute and display value
|
|
476
|
+
*/
|
|
477
|
+
export const normalizeMinuteOnBlur = (
|
|
478
|
+
inputValue: string,
|
|
479
|
+
currentMinute: number
|
|
480
|
+
): { minute: number; displayValue: string } => {
|
|
481
|
+
if (inputValue === '' || isNaN(parseInt(inputValue, 10))) {
|
|
482
|
+
return { minute: currentMinute, displayValue: currentMinute.toString().padStart(2, '0') }
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
let numValue = parseInt(inputValue, 10)
|
|
486
|
+
if (numValue > 59) numValue = 59
|
|
487
|
+
if (numValue < 0) numValue = 0
|
|
488
|
+
|
|
489
|
+
return { minute: numValue, displayValue: numValue.toString().padStart(2, '0') }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ===========================================================
|
|
493
|
+
// | Hour Format Conversion |
|
|
494
|
+
// ===========================================================
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Convert hour from 12-hour to 24-hour format
|
|
498
|
+
* @param hour - Hour in 12-hour format (1-12)
|
|
499
|
+
* @param meridiem - 'AM' or 'PM'
|
|
500
|
+
* @returns Hour in 24-hour format (0-23)
|
|
501
|
+
*/
|
|
502
|
+
export const to24Hour = (hour: number, meridiem: 'AM' | 'PM'): number => {
|
|
503
|
+
let hour24 = hour
|
|
504
|
+
if (meridiem === 'PM' && hour !== 12) hour24 = hour + 12
|
|
505
|
+
if (meridiem === 'AM' && hour === 12) hour24 = 0
|
|
506
|
+
return Math.max(0, Math.min(23, hour24))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Convert hour from 24-hour to 12-hour format
|
|
511
|
+
* @param hour24 - Hour in 24-hour format (0-23)
|
|
512
|
+
* @returns Object with hour (1-12) and meridiem ('AM' or 'PM')
|
|
513
|
+
*/
|
|
514
|
+
export const to12Hour = (hour24: number): { hour: number; meridiem: 'AM' | 'PM' } => {
|
|
515
|
+
let hour = hour24
|
|
516
|
+
let meridiem: 'AM' | 'PM' = 'AM'
|
|
517
|
+
|
|
518
|
+
if (hour24 > 12) {
|
|
519
|
+
hour = hour24 % 12
|
|
520
|
+
meridiem = 'PM'
|
|
521
|
+
} else if (hour24 === 12) {
|
|
522
|
+
meridiem = 'PM'
|
|
523
|
+
} else if (hour24 === 0) {
|
|
524
|
+
hour = 12
|
|
525
|
+
meridiem = 'AM'
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return { hour, meridiem }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Convert hour when switching from 12-hour to 24-hour format
|
|
533
|
+
* @param hour - Current hour value
|
|
534
|
+
* @param meridiem - Current meridiem
|
|
535
|
+
* @returns Conversion result with new hour and whether it changed
|
|
536
|
+
*/
|
|
537
|
+
export const convertTo24HourFormat = (
|
|
538
|
+
hour: number,
|
|
539
|
+
meridiem: 'AM' | 'PM'
|
|
540
|
+
): FormatConversionResult => {
|
|
541
|
+
let hour24 = hour
|
|
542
|
+
if (meridiem === 'PM' && hour !== 12) hour24 = hour + 12
|
|
543
|
+
if (meridiem === 'AM' && hour === 12) hour24 = 0
|
|
544
|
+
if (hour24 > 23) hour24 = 23
|
|
545
|
+
if (hour24 < 0) hour24 = 0
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
hour: hour24,
|
|
549
|
+
meridiem: hour24 < 12 ? 'AM' : 'PM',
|
|
550
|
+
changed: hour24 !== hour,
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Convert hour when switching from 24-hour to 12-hour format
|
|
556
|
+
* @param hour - Current hour value (0-23)
|
|
557
|
+
* @param currentMeridiem - Current meridiem value
|
|
558
|
+
* @returns Conversion result with new hour, meridiem, and whether it changed
|
|
559
|
+
*/
|
|
560
|
+
export const convertTo12HourFormat = (
|
|
561
|
+
hour: number,
|
|
562
|
+
currentMeridiem: 'AM' | 'PM'
|
|
563
|
+
): FormatConversionResult => {
|
|
564
|
+
let hour12 = hour
|
|
565
|
+
let newMeridiem = currentMeridiem
|
|
566
|
+
let shouldUpdateMeridiem = false
|
|
567
|
+
|
|
568
|
+
if (hour12 > 12) {
|
|
569
|
+
hour12 = hour12 % 12
|
|
570
|
+
newMeridiem = 'PM'
|
|
571
|
+
shouldUpdateMeridiem = true
|
|
572
|
+
} else if (hour12 === 0) {
|
|
573
|
+
hour12 = 12
|
|
574
|
+
newMeridiem = 'AM'
|
|
575
|
+
shouldUpdateMeridiem = true
|
|
576
|
+
} else if (hour12 === 12) {
|
|
577
|
+
// 12 in 24-hour format is 12 PM
|
|
578
|
+
newMeridiem = 'PM'
|
|
579
|
+
shouldUpdateMeridiem = true
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Clamp to valid 12-hour range
|
|
583
|
+
if (hour12 > 12) hour12 = 12
|
|
584
|
+
if (hour12 < 1) hour12 = 1
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
hour: hour12,
|
|
588
|
+
meridiem: shouldUpdateMeridiem ? newMeridiem : currentMeridiem,
|
|
589
|
+
changed: hour12 !== hour || (shouldUpdateMeridiem && newMeridiem !== currentMeridiem),
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Generate hour options based on time format
|
|
595
|
+
* @param timeFormat - 'AMPM' for 12-hour (1-12), '24hour' for 24-hour (0-23)
|
|
596
|
+
* @returns Array of valid hour numbers
|
|
597
|
+
*/
|
|
598
|
+
export const generateHourOptions = (timeFormat: TimeFormat): number[] => {
|
|
599
|
+
if (timeFormat === '24hour') {
|
|
600
|
+
return Array.from({ length: 24 }, (_, i) => i) // 0-23
|
|
601
|
+
}
|
|
602
|
+
return Array.from({ length: 12 }, (_, i) => i + 1) // 1-12
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Generate minute options (00-59)
|
|
607
|
+
* @returns Array of minute numbers 0-59
|
|
608
|
+
*/
|
|
609
|
+
export const generateMinuteOptions = (): number[] => {
|
|
610
|
+
return Array.from({ length: 60 }, (_, i) => i) // 0-59
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Determine the valid initial meridiem based on min/max time constraints
|
|
615
|
+
* If the parsed meridiem is invalid, returns a valid alternative
|
|
616
|
+
* @param parsedMeridiem - The initially parsed meridiem
|
|
617
|
+
* @param minTimeMinutes - Minimum time in minutes (or null)
|
|
618
|
+
* @param maxTimeMinutes - Maximum time in minutes (or null)
|
|
619
|
+
* @returns The valid meridiem to use
|
|
620
|
+
*/
|
|
621
|
+
export const getValidInitialMeridiem = (
|
|
622
|
+
parsedMeridiem: 'AM' | 'PM',
|
|
623
|
+
minTimeMinutes: number | null,
|
|
624
|
+
maxTimeMinutes: number | null
|
|
625
|
+
): 'AM' | 'PM' => {
|
|
626
|
+
const amValid = isAnyAMTimeValid(minTimeMinutes, maxTimeMinutes)
|
|
627
|
+
const pmValid = isAnyPMTimeValid(minTimeMinutes, maxTimeMinutes)
|
|
628
|
+
|
|
629
|
+
if (parsedMeridiem === 'AM' && amValid) return 'AM'
|
|
630
|
+
if (parsedMeridiem === 'PM' && pmValid) return 'PM'
|
|
631
|
+
|
|
632
|
+
if (!pmValid && amValid) return 'AM'
|
|
633
|
+
if (!amValid && pmValid) return 'PM'
|
|
634
|
+
|
|
635
|
+
return parsedMeridiem
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export default {
|
|
639
|
+
parseTimeToMinutes,
|
|
640
|
+
parseTime,
|
|
641
|
+
isTimeInRange,
|
|
642
|
+
isHourDisabled,
|
|
643
|
+
isAnyAMTimeValid,
|
|
644
|
+
isAnyPMTimeValid,
|
|
645
|
+
getDisplayTime,
|
|
646
|
+
get24HourTime,
|
|
647
|
+
formatTimeForDisplay,
|
|
648
|
+
getTimeRangeErrorMessage,
|
|
649
|
+
getTimezoneText,
|
|
650
|
+
getHourConstraints,
|
|
651
|
+
processHourInput,
|
|
652
|
+
normalizeHourOnBlur,
|
|
653
|
+
processMinuteInput,
|
|
654
|
+
normalizeMinuteOnBlur,
|
|
655
|
+
to24Hour,
|
|
656
|
+
to12Hour,
|
|
657
|
+
convertTo24HourFormat,
|
|
658
|
+
convertTo12HourFormat,
|
|
659
|
+
generateHourOptions,
|
|
660
|
+
generateMinuteOptions,
|
|
661
|
+
getValidInitialMeridiem,
|
|
662
|
+
}
|
|
@@ -18,6 +18,7 @@ type ItemProps = {
|
|
|
18
18
|
icon?: string,
|
|
19
19
|
iconColor?: 'default' | 'royal' | 'blue' | 'purple' | 'teal' | 'red' | 'yellow' | 'green',
|
|
20
20
|
lineStyle?: 'solid' | 'dotted',
|
|
21
|
+
showCurrentYear?: boolean,
|
|
21
22
|
} & GlobalProps
|
|
22
23
|
|
|
23
24
|
function isElementOfType<P>(
|
|
@@ -35,6 +36,7 @@ const TimelineItem = ({
|
|
|
35
36
|
icon = 'user',
|
|
36
37
|
iconColor = 'default',
|
|
37
38
|
lineStyle = 'solid',
|
|
39
|
+
showCurrentYear = false,
|
|
38
40
|
...props
|
|
39
41
|
}: ItemProps): React.ReactElement => {
|
|
40
42
|
const timelineItemCss = buildCss('pb_timeline_item_kit', lineStyle)
|
|
@@ -73,6 +75,7 @@ const TimelineItem = ({
|
|
|
73
75
|
<DateStacked
|
|
74
76
|
align="center"
|
|
75
77
|
date={date}
|
|
78
|
+
showCurrentYear={showCurrentYear}
|
|
76
79
|
size="sm"
|
|
77
80
|
/>
|
|
78
81
|
)}
|