playbook_ui 16.10.0.pre.alpha.play300617418 → 16.10.0.pre.alpha.play300617429
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/pb_date_picker/_date_picker.scss +6 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +16 -2
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +58 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +310 -64
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +54 -1
- data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +21 -5
- data/app/pb_kits/playbook/pb_dialog/_dialog_context.tsx +8 -2
- data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_header.tsx +1 -1
- data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +5 -1
- data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +125 -0
- data/app/pb_kits/playbook/pb_dialog/index.js +11 -1
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +8 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +101 -31
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.json +149 -237
- data/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json +58 -74
- data/app/pb_kits/playbook/pb_dropdown/index.js +220 -25
- data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +9 -10
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +39 -2
- data/app/pb_kits/playbook/pb_icon/docs/_playground.json +2 -16
- data/app/pb_kits/playbook/pb_icon/docs/_playground.overrides.json +2 -13
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -7
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +125 -10
- data/app/pb_kits/playbook/pb_popover/_popover.scss +27 -0
- data/app/pb_kits/playbook/pb_popover/_popover.tsx +49 -16
- data/app/pb_kits/playbook/pb_popover/index.ts +35 -4
- data/app/pb_kits/playbook/pb_popover/popover.html.erb +5 -2
- data/app/pb_kits/playbook/pb_popover/popover.test.js +212 -5
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +42 -0
- data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +309 -167
- data/app/pb_kits/playbook/pb_time_picker/index.ts +263 -11
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +109 -3
- data/app/pb_kits/playbook/pb_typeahead/kit.schema.json +6 -0
- data/app/pb_kits/playbook/utilities/floatingPortalHosts.ts +565 -0
- data/dist/chunks/{_pb_line_graph-BQrN-uXU.js → _pb_line_graph-C2H9vs_8.js} +1 -1
- data/dist/chunks/_typeahead-qxLestd5.js +5 -0
- data/dist/chunks/{globalProps-C04xFof_.js → globalProps-DIAZ03-L.js} +1 -1
- data/dist/chunks/{lib-BhWFApsB.js → lib-ZHDedNfP.js} +2 -2
- data/dist/chunks/vendor.js +5 -5
- 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/version.rb +1 -1
- metadata +7 -6
- data/dist/chunks/_typeahead-IofDd4w9.js +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc61e3b1cf418d10b8c89717465ec82d728f918711ab223e03065119d0e6ebe2
|
|
4
|
+
data.tar.gz: a59e2de7ba4dbba8477676d95664dd0442148d2effd74ad5c9f1162e7e451693
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0dd024e07261c2ead3b178c94561265f0a64caa2f07d177c71f2ef325163243c630d4903290151e7cf3eff0c70c3a1840ba56d646d31c6fff49107c37e1066e9
|
|
7
|
+
data.tar.gz: d16f9553b4b72dca3f94d39b9d3dc29529bf17c0dcb24b0154fe127e610298f9f155d1533df6e2f90a7ef6c86d5078c67b4f8dc102fe6a7a6d97f2053a5d87ac
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from 'react'
|
|
1
|
+
import React, { useContext, useEffect } from 'react'
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
|
|
4
4
|
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
@@ -6,6 +6,7 @@ import { deprecatedProps, globalProps, GlobalProps } from '../utilities/globalPr
|
|
|
6
6
|
import { getAllIcons } from "../utilities/icons/allicons"
|
|
7
7
|
import { camelToSnakeCase } from '../utilities/text'
|
|
8
8
|
|
|
9
|
+
import { DialogContext } from '../pb_dialog/_dialog_context'
|
|
9
10
|
import datePickerHelper from './date_picker_helper'
|
|
10
11
|
import Icon from '../pb_icon/_icon'
|
|
11
12
|
import Caption from '../pb_caption/_caption'
|
|
@@ -149,6 +150,8 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
|
|
|
149
150
|
syncEndWith,
|
|
150
151
|
} = props
|
|
151
152
|
|
|
153
|
+
const dialogCtx = useContext(DialogContext)
|
|
154
|
+
|
|
152
155
|
const ariaProps = buildAriaProps(aria)
|
|
153
156
|
const normalizedDefaultDate = normalizeDefaultDate(defaultDate)
|
|
154
157
|
const filterResetDefaultSerialized = serializeDefaultDateForFilterReset(normalizedDefaultDate)
|
|
@@ -186,6 +189,7 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
|
|
|
186
189
|
format,
|
|
187
190
|
hideIcon,
|
|
188
191
|
inLine,
|
|
192
|
+
inline: inLine,
|
|
189
193
|
maxDate,
|
|
190
194
|
minDate,
|
|
191
195
|
mode,
|
|
@@ -208,8 +212,18 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
|
|
|
208
212
|
syncStartWith,
|
|
209
213
|
syncEndWith,
|
|
210
214
|
required: false,
|
|
215
|
+
dialogPortalTarget: dialogCtx?.selectMenuPortalTarget ?? null,
|
|
211
216
|
}, scrollContainer)
|
|
212
|
-
|
|
217
|
+
|
|
218
|
+
return () => {
|
|
219
|
+
const input = document.getElementById(String(pickerId)) as
|
|
220
|
+
| (HTMLElement & { _flatpickr?: { destroy: () => void }; _pbDatePickerOpenUnsub?: () => void })
|
|
221
|
+
| null
|
|
222
|
+
input?._pbDatePickerOpenUnsub?.()
|
|
223
|
+
delete input?._pbDatePickerOpenUnsub
|
|
224
|
+
input?._flatpickr?.destroy()
|
|
225
|
+
}
|
|
226
|
+
}, initializeOnce ? [initializeOnce, dialogCtx?.selectMenuPortalTarget, pickerId] : undefined)
|
|
213
227
|
|
|
214
228
|
const filteredProps = {...props}
|
|
215
229
|
if (filteredProps.marginBottom === undefined) {
|
|
@@ -96,22 +96,73 @@
|
|
|
96
96
|
})
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
const disconnectPopoverWatch = (input) => {
|
|
100
|
+
if (!input) return
|
|
101
|
+
|
|
102
|
+
if (input._pbDatePickerPopoverObserver) {
|
|
103
|
+
input._pbDatePickerPopoverObserver.disconnect()
|
|
104
|
+
delete input._pbDatePickerPopoverObserver
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
delete input.dataset.pbDatePickerPopoverWatch
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
const loadDatePicker = () => {
|
|
100
111
|
const input = document.getElementById("<%= object.picker_id %>")
|
|
101
112
|
|
|
102
|
-
if (!input || input._flatpickr)
|
|
113
|
+
if (!input || input._flatpickr) {
|
|
114
|
+
disconnectPopoverWatch(input)
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
103
117
|
if (typeof window.datePickerHelper !== "function") return false
|
|
104
118
|
|
|
119
|
+
const popoverTooltip = input.closest(".pb_popover_tooltip")
|
|
120
|
+
if (popoverTooltip && !popoverTooltip.classList.contains("show")) {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
105
124
|
window.datePickerHelper(<%= object.date_picker_config %>, "<%= object.scroll_container %>")
|
|
106
125
|
initQuickPickChangeListener(input)
|
|
126
|
+
disconnectPopoverWatch(input)
|
|
107
127
|
|
|
108
128
|
return true
|
|
109
129
|
}
|
|
110
130
|
|
|
131
|
+
const watchPopoverVisibility = () => {
|
|
132
|
+
const input = document.getElementById("<%= object.picker_id %>")
|
|
133
|
+
if (!input || input.dataset.pbDatePickerPopoverWatch) return
|
|
134
|
+
|
|
135
|
+
const popoverTooltip = input.closest(".pb_popover_tooltip")
|
|
136
|
+
if (!popoverTooltip) return
|
|
137
|
+
|
|
138
|
+
input.dataset.pbDatePickerPopoverWatch = "true"
|
|
139
|
+
const tryInitWhenVisible = () => {
|
|
140
|
+
if (!input.isConnected || !popoverTooltip.isConnected) {
|
|
141
|
+
disconnectPopoverWatch(input)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (popoverTooltip.classList.contains("show")) {
|
|
146
|
+
loadDatePicker()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const observer = new MutationObserver(tryInitWhenVisible)
|
|
151
|
+
input._pbDatePickerPopoverObserver = observer
|
|
152
|
+
observer.observe(popoverTooltip, { attributes: true, attributeFilter: ["class"] })
|
|
153
|
+
tryInitWhenVisible()
|
|
154
|
+
}
|
|
155
|
+
|
|
111
156
|
let attempts = 0
|
|
112
157
|
const retryLoad = () => {
|
|
113
158
|
if (loadDatePicker()) return
|
|
114
159
|
|
|
160
|
+
const input = document.getElementById("<%= object.picker_id %>")
|
|
161
|
+
if (input?.closest(".pb_popover_tooltip")) {
|
|
162
|
+
watchPopoverVisibility()
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
115
166
|
if (attempts++ < 20) {
|
|
116
167
|
setTimeout(retryLoad, 100)
|
|
117
168
|
}
|
|
@@ -134,6 +185,12 @@
|
|
|
134
185
|
if (<%= !object.custom_event_type.empty? %>) {
|
|
135
186
|
window.addEventListener("<%= object.custom_event_type %>", retryLoad)
|
|
136
187
|
}
|
|
188
|
+
|
|
189
|
+
document.addEventListener("turbo:before-cache", () => {
|
|
190
|
+
const input = document.getElementById("<%= object.picker_id %>")
|
|
191
|
+
disconnectPopoverWatch(input)
|
|
192
|
+
input?._flatpickr?.destroy()
|
|
193
|
+
})
|
|
137
194
|
})()
|
|
138
195
|
<% end %>
|
|
139
196
|
<% end %>
|
|
@@ -6,6 +6,18 @@ import weekSelect from "flatpickr/dist/plugins/weekSelect/weekSelect"
|
|
|
6
6
|
import timeSelectPlugin from './plugins/timeSelect'
|
|
7
7
|
import quickPickPlugin from './plugins/quickPick'
|
|
8
8
|
import { getAllIcons } from '../utilities/icons/allicons';
|
|
9
|
+
import {
|
|
10
|
+
announceFloatingKitOpen,
|
|
11
|
+
nextPortaledFloatingZIndex,
|
|
12
|
+
positionDropdownPortalToWrapper,
|
|
13
|
+
resolveFloatingOwnerId,
|
|
14
|
+
resolvePortaledFloatingZIndex,
|
|
15
|
+
resolvePortaledKitHost,
|
|
16
|
+
kitRequiresPortaledFloatingUi,
|
|
17
|
+
setFloatingOwnerAttribute,
|
|
18
|
+
subscribeFloatingKitOpen,
|
|
19
|
+
subscribeFloatingKitReposition,
|
|
20
|
+
} from '../utilities/floatingPortalHosts'
|
|
9
21
|
|
|
10
22
|
const { angleDown, angleLeft, angleRight } = getAllIcons()
|
|
11
23
|
const angleDownString = angleDown.string
|
|
@@ -41,7 +53,9 @@ type DatePickerConfig = {
|
|
|
41
53
|
controlsEndId?: string,
|
|
42
54
|
syncStartWith?: string,
|
|
43
55
|
syncEndWith?: string,
|
|
44
|
-
|
|
56
|
+
/** React Dialog floating root; omit in Rails (DOM resolution only). */
|
|
57
|
+
dialogPortalTarget?: HTMLElement | null,
|
|
58
|
+
} & Pick<BaseOptions, "allowInput" | "defaultDate" | "enableTime" | "maxDate" | "minDate" | "mode" | "plugins" | "position" | "positionElement" | "inline" >
|
|
45
59
|
|
|
46
60
|
const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HTMLElement) => {
|
|
47
61
|
const noop = () => {
|
|
@@ -80,8 +94,92 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
80
94
|
controlsEndId,
|
|
81
95
|
syncStartWith,
|
|
82
96
|
syncEndWith,
|
|
97
|
+
dialogPortalTarget,
|
|
98
|
+
inline = false,
|
|
83
99
|
} = config
|
|
84
100
|
|
|
101
|
+
const inputElForPortal =
|
|
102
|
+
typeof document !== "undefined"
|
|
103
|
+
? document.querySelector<HTMLElement>(`#${String(pickerId)}`)
|
|
104
|
+
: null
|
|
105
|
+
const kitRootForPortal = inputElForPortal?.closest(".pb_date_picker_kit") as HTMLElement | null
|
|
106
|
+
const portalHost =
|
|
107
|
+
!inline && kitRootForPortal && kitRequiresPortaledFloatingUi(kitRootForPortal)
|
|
108
|
+
? resolvePortaledKitHost(kitRootForPortal, dialogPortalTarget ?? null)
|
|
109
|
+
: null
|
|
110
|
+
|
|
111
|
+
const floatingOwnerId = resolveFloatingOwnerId(kitRootForPortal)
|
|
112
|
+
const effectiveStatic = portalHost ? false : staticPosition
|
|
113
|
+
|
|
114
|
+
type DatePickerInputEl = HTMLElement & {
|
|
115
|
+
_flatpickr?: Instance
|
|
116
|
+
_pbDatePickerOpenUnsub?: () => void
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const existingInput = document.querySelector(`#${String(pickerId)}`) as DatePickerInputEl | null
|
|
120
|
+
if (existingInput?._pbDatePickerOpenUnsub) {
|
|
121
|
+
existingInput._pbDatePickerOpenUnsub()
|
|
122
|
+
delete existingInput._pbDatePickerOpenUnsub
|
|
123
|
+
}
|
|
124
|
+
if (existingInput?._flatpickr) {
|
|
125
|
+
existingInput._flatpickr.destroy()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let portalAppendShell: HTMLElement | undefined
|
|
129
|
+
|
|
130
|
+
const removePortalShell = (): void => {
|
|
131
|
+
if (!portalHost) return
|
|
132
|
+
|
|
133
|
+
const shellSelector = `[data-pb-date-picker-floating-shell="${String(pickerId)}"]`
|
|
134
|
+
const shell =
|
|
135
|
+
portalAppendShell?.isConnected
|
|
136
|
+
? portalAppendShell
|
|
137
|
+
: (portalHost.querySelector(shellSelector) as HTMLElement | undefined)
|
|
138
|
+
|
|
139
|
+
shell?.remove()
|
|
140
|
+
portalAppendShell = undefined
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const ensurePortalShell = (): HTMLElement | undefined => {
|
|
144
|
+
if (!portalHost) return undefined
|
|
145
|
+
if (portalAppendShell?.isConnected) return portalAppendShell
|
|
146
|
+
|
|
147
|
+
const shellSelector = `[data-pb-date-picker-floating-shell="${String(pickerId)}"]`
|
|
148
|
+
portalAppendShell =
|
|
149
|
+
portalHost.querySelector(shellSelector) as HTMLElement | undefined
|
|
150
|
+
|
|
151
|
+
if (!portalAppendShell) {
|
|
152
|
+
portalAppendShell = document.createElement("div")
|
|
153
|
+
portalAppendShell.className = "pb_date_picker_floating_shell pb_date_picker_kit"
|
|
154
|
+
portalAppendShell.setAttribute(
|
|
155
|
+
"data-pb-date-picker-floating-shell",
|
|
156
|
+
String(pickerId),
|
|
157
|
+
)
|
|
158
|
+
portalAppendShell.addEventListener("mousedown", (e) => {
|
|
159
|
+
e.stopPropagation()
|
|
160
|
+
})
|
|
161
|
+
portalAppendShell.addEventListener("click", (e) => {
|
|
162
|
+
e.stopPropagation()
|
|
163
|
+
})
|
|
164
|
+
portalHost.appendChild(portalAppendShell)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
setFloatingOwnerAttribute(portalAppendShell, floatingOwnerId)
|
|
168
|
+
return portalAppendShell
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const mountCalendarInPortalShell = (fp: Instance): void => {
|
|
172
|
+
const shell = ensurePortalShell()
|
|
173
|
+
if (!shell?.isConnected || !fp.calendarContainer) return
|
|
174
|
+
if (fp.calendarContainer.parentElement !== shell) {
|
|
175
|
+
shell.appendChild(fp.calendarContainer)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let activePortalZIndex: string | undefined
|
|
180
|
+
|
|
181
|
+
let unsubscribeFloatingReposition: (() => void) | null = null
|
|
182
|
+
|
|
85
183
|
// ===========================================================
|
|
86
184
|
// | Hook Definitions |
|
|
87
185
|
// ===========================================================
|
|
@@ -188,6 +286,12 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
188
286
|
}
|
|
189
287
|
|
|
190
288
|
const positionCalendarIfNeeded = (fp: Instance) => {
|
|
289
|
+
if (portalHost) {
|
|
290
|
+
mountCalendarInPortalShell(fp)
|
|
291
|
+
fp._positionCalendar()
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
191
295
|
const cal = document.querySelector(`#cal-${pickerId}`) as HTMLElement
|
|
192
296
|
if (!cal) return
|
|
193
297
|
|
|
@@ -197,7 +301,7 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
197
301
|
const spaceAbove = inputRect.top
|
|
198
302
|
|
|
199
303
|
if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
|
|
200
|
-
if (
|
|
304
|
+
if (effectiveStatic) {
|
|
201
305
|
cal.style.top = 'auto'
|
|
202
306
|
cal.style.bottom = 'calc(100% + 5px)'
|
|
203
307
|
} else {
|
|
@@ -205,7 +309,7 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
205
309
|
cal.style.top = `${Math.max(10, inputRect.top - h - 5)}px`
|
|
206
310
|
cal.style.left = `${inputRect.left}px`
|
|
207
311
|
}
|
|
208
|
-
} else if (
|
|
312
|
+
} else if (effectiveStatic) {
|
|
209
313
|
cal.style.top = ''
|
|
210
314
|
cal.style.bottom = ''
|
|
211
315
|
} else {
|
|
@@ -239,9 +343,10 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
239
343
|
}
|
|
240
344
|
|
|
241
345
|
// Attach / detach to / from scroll events
|
|
242
|
-
const initialPicker = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)._flatpickr
|
|
243
346
|
const scrollEvent = () => {
|
|
244
|
-
|
|
347
|
+
const fp = document.querySelector<HTMLElement & { _flatpickr?: Instance }>(`#${pickerId}`)
|
|
348
|
+
?._flatpickr
|
|
349
|
+
fp?._positionCalendar()
|
|
245
350
|
}
|
|
246
351
|
function attachToScroll(scrollParent: string | HTMLElement) {
|
|
247
352
|
document.querySelectorAll(scrollParent as string)[0]?.addEventListener("scroll", scrollEvent, { passive: true })
|
|
@@ -250,10 +355,23 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
250
355
|
document.querySelectorAll(scrollParent as string)[0]?.removeEventListener("scroll", scrollEvent)
|
|
251
356
|
}
|
|
252
357
|
|
|
358
|
+
const yearSelectId = `year-${pickerId}`
|
|
359
|
+
|
|
360
|
+
const yearDropdownFromCalendar = (fp: Instance): HTMLSelectElement | null => {
|
|
361
|
+
return fp.calendarContainer?.querySelector<HTMLSelectElement>(`#${yearSelectId}`) ?? null
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const yearDropdownIsReady = (fp: Instance): boolean => {
|
|
365
|
+
const dropdown = yearDropdownFromCalendar(fp)
|
|
366
|
+
return dropdown !== null && dropdown.options.length > 0
|
|
367
|
+
}
|
|
368
|
+
|
|
253
369
|
// two way binding
|
|
254
370
|
const yearChangeHook = (fp: Instance) => {
|
|
255
|
-
|
|
371
|
+
const yearInput = yearDropdownFromCalendar(fp)
|
|
372
|
+
if (yearInput) {
|
|
256
373
|
yearInput.value = fp.currentYear?.toString()
|
|
374
|
+
}
|
|
257
375
|
}
|
|
258
376
|
|
|
259
377
|
const handleDatePickerChange = (fp: Instance, selectedDates: Date[]) => {
|
|
@@ -355,7 +473,94 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
355
473
|
: setMaxDate
|
|
356
474
|
|
|
357
475
|
// End of Default Date + Min/Max Date Initialization Helper Functions section ----/
|
|
358
|
-
|
|
476
|
+
const resolveDatePickerPortalWrapper = (input: HTMLElement): HTMLElement | null => {
|
|
477
|
+
return (
|
|
478
|
+
(input.closest(".date_picker_input_wrapper") as HTMLElement | null) ??
|
|
479
|
+
(input.closest(".text_input_wrapper") as HTMLElement | null) ??
|
|
480
|
+
(input.closest(".input_wrapper") as HTMLElement | null) ??
|
|
481
|
+
(input.closest(".pb_date_picker_kit") as HTMLElement | null)
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const portalPositionFn: ((self: Instance) => void) | undefined = portalHost
|
|
486
|
+
? (self: Instance) => {
|
|
487
|
+
mountCalendarInPortalShell(self)
|
|
488
|
+
const shell = ensurePortalShell()
|
|
489
|
+
const cal = self.calendarContainer
|
|
490
|
+
if (!cal || !shell) return
|
|
491
|
+
const wrap = resolveDatePickerPortalWrapper(self.input)
|
|
492
|
+
const host = shell.parentElement as HTMLElement | null
|
|
493
|
+
if (!wrap || !host) return
|
|
494
|
+
positionDropdownPortalToWrapper({
|
|
495
|
+
panel: cal,
|
|
496
|
+
wrapperViewportRect: wrap.getBoundingClientRect(),
|
|
497
|
+
positionHost: host,
|
|
498
|
+
matchWrapperWidth: selectionType === "quickpick",
|
|
499
|
+
zIndex: activePortalZIndex,
|
|
500
|
+
})
|
|
501
|
+
}
|
|
502
|
+
: undefined
|
|
503
|
+
|
|
504
|
+
const setupYearMonthDropdowns = (fp: Instance) => {
|
|
505
|
+
if (yearDropdownIsReady(fp) || !fp.yearElements?.[0]?.parentElement) return
|
|
506
|
+
|
|
507
|
+
fp.yearElements[0].parentElement.innerHTML = `<select class="numInput cur-year" type="number" tabIndex="-1" aria-label="Year" id="${yearSelectId}"></select>`
|
|
508
|
+
|
|
509
|
+
let years = ''
|
|
510
|
+
if (yearAscending) {
|
|
511
|
+
for (let year = setMinYear; year <= setMaxYear; year++) {
|
|
512
|
+
years += `<option value="${year}">${year}</option>`
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
for (let year = setMaxYear; year >= setMinYear; year--) {
|
|
516
|
+
years += `<option value="${year}">${year}</option>`
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const dropdown = yearDropdownFromCalendar(fp)
|
|
521
|
+
if (!dropdown) return
|
|
522
|
+
|
|
523
|
+
dropdown.innerHTML = years
|
|
524
|
+
dropdown.value = String(fp.currentYear)
|
|
525
|
+
|
|
526
|
+
dropdown.addEventListener('input', (e: Event & { target: { value: string }}) => {
|
|
527
|
+
fp.changeYear(Number(e.target.value))
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
if (fp.input.form) {
|
|
531
|
+
fp.input.form.addEventListener('reset', () => {
|
|
532
|
+
setTimeout(() => {
|
|
533
|
+
dropdown.value = String(fp.currentYear)
|
|
534
|
+
if (fp.monthsDropdownContainer) {
|
|
535
|
+
fp.monthsDropdownContainer.value = String(fp.currentMonth)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (defaultDate) {
|
|
539
|
+
fp.setDate(defaultDate)
|
|
540
|
+
yearChangeHook(fp)
|
|
541
|
+
}
|
|
542
|
+
}, 0)
|
|
543
|
+
})
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (!dropdown.nextElementSibling?.classList.contains('year-dropdown-icon')) {
|
|
547
|
+
dropdown.insertAdjacentHTML('afterend', `<i class="year-dropdown-icon">${angleDownString}</i>`)
|
|
548
|
+
}
|
|
549
|
+
if (
|
|
550
|
+
fp.monthElements[0]?.parentElement &&
|
|
551
|
+
!fp.calendarContainer?.querySelector('.month-dropdown-icon')
|
|
552
|
+
) {
|
|
553
|
+
fp.monthElements[0].insertAdjacentHTML('afterend', `<i class="month-dropdown-icon">${angleDownString}</i>`)
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const assignCalendarId = (fp: Instance): void => {
|
|
558
|
+
const calEl = fp.calendarContainer ?? fp.innerContainer?.parentElement
|
|
559
|
+
if (calEl) {
|
|
560
|
+
calEl.id = `cal-${pickerId}`
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
359
564
|
flatpickr(`#${pickerId}`, {
|
|
360
565
|
allowInput,
|
|
361
566
|
closeOnSelect,
|
|
@@ -372,6 +577,25 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
372
577
|
mode,
|
|
373
578
|
nextArrow: `<div style="height: 14px;">${angleRightString}</div>`,
|
|
374
579
|
onOpen: [(_selectedDates, _dateStr, fp) => {
|
|
580
|
+
activePortalZIndex = portalHost
|
|
581
|
+
? resolvePortaledFloatingZIndex(
|
|
582
|
+
portalHost,
|
|
583
|
+
nextPortaledFloatingZIndex(),
|
|
584
|
+
)
|
|
585
|
+
: undefined
|
|
586
|
+
if (portalHost) {
|
|
587
|
+
mountCalendarInPortalShell(fp)
|
|
588
|
+
}
|
|
589
|
+
if (portalAppendShell) {
|
|
590
|
+
portalAppendShell.style.zIndex = activePortalZIndex
|
|
591
|
+
setFloatingOwnerAttribute(portalAppendShell, floatingOwnerId)
|
|
592
|
+
}
|
|
593
|
+
if (fp.calendarContainer) {
|
|
594
|
+
fp.calendarContainer.style.zIndex = activePortalZIndex
|
|
595
|
+
setFloatingOwnerAttribute(fp.calendarContainer, floatingOwnerId)
|
|
596
|
+
}
|
|
597
|
+
announceFloatingKitOpen('date-picker', floatingOwnerId)
|
|
598
|
+
|
|
375
599
|
// If defaultDate was out of range of a dev set min/max date, restore it when calendar opens (in situation where the input was manually cleared or the calendar was closed without selection)
|
|
376
600
|
if (hasOutOfRangeDefault) {
|
|
377
601
|
const dateObj = toDateObject(defaultDateValue)
|
|
@@ -402,15 +626,37 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
402
626
|
positionCalendarIfNeeded(fp)
|
|
403
627
|
}
|
|
404
628
|
window.addEventListener('resize', resizeRepositionHandlerRef)
|
|
405
|
-
if (!
|
|
629
|
+
if (!effectiveStatic && scrollContainer) attachToScroll(scrollContainer)
|
|
630
|
+
if (portalHost) {
|
|
631
|
+
unsubscribeFloatingReposition = subscribeFloatingKitReposition(() => {
|
|
632
|
+
positionCalendarIfNeeded(fp)
|
|
633
|
+
})
|
|
634
|
+
}
|
|
635
|
+
assignCalendarId(fp)
|
|
406
636
|
positionCalendarIfNeeded(fp)
|
|
637
|
+
setupYearMonthDropdowns(fp)
|
|
638
|
+
}],
|
|
639
|
+
onDestroy: [() => {
|
|
640
|
+
if (unsubscribeFloatingReposition) {
|
|
641
|
+
unsubscribeFloatingReposition()
|
|
642
|
+
unsubscribeFloatingReposition = null
|
|
643
|
+
}
|
|
644
|
+
if (resizeRepositionHandlerRef) {
|
|
645
|
+
window.removeEventListener('resize', resizeRepositionHandlerRef)
|
|
646
|
+
resizeRepositionHandlerRef = null
|
|
647
|
+
}
|
|
648
|
+
removePortalShell()
|
|
407
649
|
}],
|
|
408
650
|
onClose: [(selectedDates, dateStr, fp) => {
|
|
651
|
+
if (unsubscribeFloatingReposition) {
|
|
652
|
+
unsubscribeFloatingReposition()
|
|
653
|
+
unsubscribeFloatingReposition = null
|
|
654
|
+
}
|
|
409
655
|
if (resizeRepositionHandlerRef) {
|
|
410
656
|
window.removeEventListener('resize', resizeRepositionHandlerRef)
|
|
411
657
|
resizeRepositionHandlerRef = null
|
|
412
658
|
}
|
|
413
|
-
if (!
|
|
659
|
+
if (!effectiveStatic && scrollContainer) detachFromScroll(scrollContainer as HTMLElement)
|
|
414
660
|
|
|
415
661
|
// If defaultDate was out of range and no date was selected, preserve the default date
|
|
416
662
|
if (hasOutOfRangeDefault && (!selectedDates || selectedDates.length === 0)) {
|
|
@@ -440,15 +686,17 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
440
686
|
yearChangeHook(fp)
|
|
441
687
|
}],
|
|
442
688
|
plugins: setPlugins(thisRangesEndToday, customQuickPickDates),
|
|
443
|
-
position,
|
|
689
|
+
position: portalPositionFn ?? position,
|
|
444
690
|
positionElement: getPositionElement(positionElement),
|
|
445
691
|
prevArrow: `<div style="height: 14px;">${angleLeftString}</div>`,
|
|
446
|
-
static:
|
|
692
|
+
static: effectiveStatic,
|
|
447
693
|
})
|
|
448
694
|
|
|
449
695
|
// Assign dynamically sourced flatpickr instance to variable
|
|
450
|
-
const picker = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)
|
|
451
|
-
picker
|
|
696
|
+
const picker = document.querySelector<HTMLElement & { [x: string]: any }>(`#${pickerId}`)?._flatpickr
|
|
697
|
+
if (!picker) return
|
|
698
|
+
|
|
699
|
+
assignCalendarId(picker)
|
|
452
700
|
|
|
453
701
|
// If defaultDate was out of range, restore the original minDate/maxDate after initialization (defaultDate displayed, still cannot select dates outside the actual range via user provided minDate/maxDate constraints)
|
|
454
702
|
if ((isBeforeMin || isAfterMax) && defaultDateValue) {
|
|
@@ -515,51 +763,7 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
515
763
|
}, 10)
|
|
516
764
|
}
|
|
517
765
|
|
|
518
|
-
|
|
519
|
-
picker.yearElements[0].parentElement.innerHTML = `<select class="numInput cur-year" type="number" tabIndex="-1" aria-label="Year" id="year-${pickerId}"></select>`
|
|
520
|
-
|
|
521
|
-
// create html option tags for desired years
|
|
522
|
-
let years = ''
|
|
523
|
-
if (yearAscending) {
|
|
524
|
-
for (let year = setMinYear; year <= setMaxYear; year++) {
|
|
525
|
-
years += `<option value="${year}">${year}</option>`
|
|
526
|
-
}
|
|
527
|
-
} else {
|
|
528
|
-
for (let year = setMaxYear; year >= setMinYear; year--) {
|
|
529
|
-
years += `<option value="${year}">${year}</option>`
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// variablize each dropdown selector
|
|
534
|
-
const dropdown = document.querySelector<HTMLElement & { [x: string]: any }>(`#year-${pickerId}`)
|
|
535
|
-
|
|
536
|
-
// inject year options into dropdown and assign it the flatpickr's current year value
|
|
537
|
-
dropdown.innerHTML = years
|
|
538
|
-
dropdown.value = picker.currentYear
|
|
539
|
-
|
|
540
|
-
// whenever a new year is selected from dropdown update flatpickr's current year value
|
|
541
|
-
dropdown.addEventListener('input', (e: Event & { target: { value: string}}) => {
|
|
542
|
-
picker.changeYear(Number(e.target.value))
|
|
543
|
-
})
|
|
544
|
-
|
|
545
|
-
// Reverse month and year dropdown reset on form.reset()
|
|
546
|
-
if (picker.input.form) {
|
|
547
|
-
picker.input.form.addEventListener('reset', () => {
|
|
548
|
-
// Code block triggers after form.reset() is called and executed
|
|
549
|
-
setTimeout(() => {
|
|
550
|
-
dropdown.value = picker.currentYear
|
|
551
|
-
if (picker.monthsDropdownContainer) {
|
|
552
|
-
picker.monthsDropdownContainer.value = picker.currentMonth
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/* Reset date picker to default value on form.reset() */
|
|
556
|
-
if (defaultDate){
|
|
557
|
-
picker.setDate(defaultDate)
|
|
558
|
-
yearChangeHook(picker)
|
|
559
|
-
}
|
|
560
|
-
}, 0)
|
|
561
|
-
})
|
|
562
|
-
}
|
|
766
|
+
setupYearMonthDropdowns(picker)
|
|
563
767
|
|
|
564
768
|
// === Automatic Sync Logic for 3 input range pattern===
|
|
565
769
|
|
|
@@ -661,11 +865,6 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
661
865
|
}
|
|
662
866
|
}
|
|
663
867
|
|
|
664
|
-
// Adding dropdown icons to year and month select
|
|
665
|
-
dropdown.insertAdjacentHTML('afterend', `<i class="year-dropdown-icon">${angleDownString}</i>`)
|
|
666
|
-
if (picker.monthElements[0].parentElement) {
|
|
667
|
-
return picker.monthElements[0].insertAdjacentHTML('afterend', `<i class="month-dropdown-icon">${angleDownString}</i>`)}
|
|
668
|
-
|
|
669
868
|
// Remove readonly attribute for validation and or text input
|
|
670
869
|
if (allowInput){
|
|
671
870
|
picker.input.removeAttribute('readonly')
|
|
@@ -677,8 +876,55 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
|
|
|
677
876
|
picker.input.style.cursor = 'pointer'
|
|
678
877
|
}
|
|
679
878
|
|
|
879
|
+
const stopPointerForFlatpickrDocClose = (e: Event) => {
|
|
880
|
+
e.stopPropagation()
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Prevent flatpickr's document mousedown listener from treating the opening click as outside.
|
|
884
|
+
picker.input.addEventListener('mousedown', stopPointerForFlatpickrDocClose)
|
|
885
|
+
picker.input.addEventListener('touchstart', stopPointerForFlatpickrDocClose, { passive: true })
|
|
886
|
+
|
|
887
|
+
const calIconWrapper = document.querySelector(`#cal-icon-${pickerId}`) as HTMLElement | null
|
|
888
|
+
const isPointerInCalIcon = (e: MouseEvent) => {
|
|
889
|
+
if (!calIconWrapper) return false
|
|
890
|
+
const rect = calIconWrapper.getBoundingClientRect()
|
|
891
|
+
return (
|
|
892
|
+
e.clientX >= rect.left &&
|
|
893
|
+
e.clientX <= rect.right &&
|
|
894
|
+
e.clientY >= rect.top &&
|
|
895
|
+
e.clientY <= rect.bottom
|
|
896
|
+
)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const openDatePickerFromUi = (e: MouseEvent) => {
|
|
900
|
+
e.preventDefault()
|
|
901
|
+
e.stopPropagation()
|
|
902
|
+
if (picker.input.disabled) return
|
|
903
|
+
if (picker.isOpen) {
|
|
904
|
+
picker.close()
|
|
905
|
+
return
|
|
906
|
+
}
|
|
907
|
+
picker.open()
|
|
908
|
+
picker.input.focus()
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const textInputWrapper = picker.input.closest('.text_input_wrapper') as HTMLElement | null
|
|
912
|
+
const inputWrapper = picker.input.closest('.input_wrapper') as HTMLElement | null
|
|
913
|
+
inputWrapper?.addEventListener('click', (e: MouseEvent) => {
|
|
914
|
+
if (e.target === picker.input) return
|
|
915
|
+
if (!isPointerInCalIcon(e) && !textInputWrapper?.contains(e.target as Node)) return
|
|
916
|
+
openDatePickerFromUi(e)
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
const pickerInput = picker.input as DatePickerInputEl
|
|
920
|
+
pickerInput._pbDatePickerOpenUnsub = subscribeFloatingKitOpen(({ kitKind }) => {
|
|
921
|
+
if (kitKind !== 'date-picker' && picker.isOpen) {
|
|
922
|
+
picker.close()
|
|
923
|
+
}
|
|
924
|
+
})
|
|
925
|
+
|
|
680
926
|
// Fix event bubbling bug on wrapper
|
|
681
|
-
document.querySelector(`#${pickerId}`)
|
|
927
|
+
document.querySelector(`#${pickerId}`)?.parentElement?.addEventListener('click', (e) => e.stopPropagation())
|
|
682
928
|
}
|
|
683
929
|
|
|
684
930
|
export default datePickerHelper
|