stalefish 8.0.2 → 8.0.4
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.
- package/adapters/flatpickr.mjs +104 -0
- package/components/dateTimePicker.mjs +16 -2
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatpickr adapter for Stalefish (halfcab/lit-based)
|
|
3
|
+
*
|
|
4
|
+
* Goals:
|
|
5
|
+
* - Always initialize flatpickr on a real Element (not a DocumentFragment/TemplateResult)
|
|
6
|
+
* - Defer initialization until the node is connected, to be SSR/DOM-safe
|
|
7
|
+
* - Store the instance on the wrapper element (`wrapper._flatpickr`) consistently
|
|
8
|
+
* - Provide simple attach/detach helpers for reuse across components
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import flatpickr from 'flatpickr'
|
|
12
|
+
* import { attachFlatpickr, detachFlatpickr } from '../adapters/flatpickr.mjs'
|
|
13
|
+
*
|
|
14
|
+
* const wrapper = el.firstElementChild // your component's outer <div>
|
|
15
|
+
* const instance = attachFlatpickr(wrapper, flatpickr, { wrap: true, ...options })
|
|
16
|
+
* // later on removal:
|
|
17
|
+
* detachFlatpickr(wrapper)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the actual wrapper Element to pass to flatpickr.
|
|
22
|
+
* Accepts either an Element, or a container whose firstElementChild is the wrapper.
|
|
23
|
+
* Falls back to querySelector for common markers if necessary.
|
|
24
|
+
* @param {any} node
|
|
25
|
+
* @returns {Element|null}
|
|
26
|
+
*/
|
|
27
|
+
function resolveWrapperElement (node) {
|
|
28
|
+
if (!node) return null
|
|
29
|
+
// If it's already an Element (HTMLElement/SVGElement), use it directly
|
|
30
|
+
if (typeof Element !== 'undefined' && node instanceof Element) return node
|
|
31
|
+
// If it's a container/fragment with a firstElementChild, use that
|
|
32
|
+
if (node.firstElementChild) return node.firstElementChild
|
|
33
|
+
// Last resort: try to find a likely wrapper inside
|
|
34
|
+
if (node.querySelector) {
|
|
35
|
+
return node.querySelector('[data-flatpickr-wrapper], [data-input]')
|
|
36
|
+
}
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Attaches or updates a flatpickr instance on the given wrapper/container.
|
|
42
|
+
* The instance is stored on `wrapperEl._flatpickr`.
|
|
43
|
+
*
|
|
44
|
+
* @param {Element|any} wrapperOrContainer - An Element or a container whose firstElementChild is the wrapper
|
|
45
|
+
* @param {Function} flatpickrLib - The flatpickr factory function (import flatpickr from 'flatpickr')
|
|
46
|
+
* @param {Object} config - Flatpickr configuration options
|
|
47
|
+
* @returns {Object|null} flatpickr instance, or null if deferred/not created
|
|
48
|
+
*/
|
|
49
|
+
export function attachFlatpickr (wrapperOrContainer, flatpickrLib, config = {}) {
|
|
50
|
+
if (typeof window === 'undefined') return null // SSR guard
|
|
51
|
+
if (!flatpickrLib) return null
|
|
52
|
+
|
|
53
|
+
const wrapperEl = resolveWrapperElement(wrapperOrContainer)
|
|
54
|
+
if (!wrapperEl) return null
|
|
55
|
+
|
|
56
|
+
// If instance already exists, try to update options where supported
|
|
57
|
+
if (wrapperEl._flatpickr) {
|
|
58
|
+
try {
|
|
59
|
+
// Not all options are dynamically settable, but this is a safe best-effort
|
|
60
|
+
wrapperEl._flatpickr.set(config)
|
|
61
|
+
} catch (e) {
|
|
62
|
+
try {
|
|
63
|
+
wrapperEl._flatpickr.destroy()
|
|
64
|
+
} catch (_) {}
|
|
65
|
+
wrapperEl._flatpickr = null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const init = () => {
|
|
70
|
+
// Ensure wrap:true is preserved if provided; caller decides
|
|
71
|
+
const instance = flatpickrLib(wrapperEl, config)
|
|
72
|
+
wrapperEl._flatpickr = instance
|
|
73
|
+
return instance
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If not connected yet, defer until the next frame when likely connected
|
|
77
|
+
if (!wrapperEl.isConnected) {
|
|
78
|
+
requestAnimationFrame(() => {
|
|
79
|
+
if (!wrapperEl._flatpickr && wrapperEl.isConnected) {
|
|
80
|
+
try { init() } catch (e) { /* swallow init errors in deferred path */ }
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return init()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Destroys and detaches a flatpickr instance from the wrapper element, if present.
|
|
91
|
+
* @param {Element|any} wrapperOrContainer
|
|
92
|
+
*/
|
|
93
|
+
export function detachFlatpickr (wrapperOrContainer) {
|
|
94
|
+
const wrapperEl = resolveWrapperElement(wrapperOrContainer)
|
|
95
|
+
if (wrapperEl && wrapperEl._flatpickr) {
|
|
96
|
+
try { wrapperEl._flatpickr.destroy() } catch (e) {}
|
|
97
|
+
try { delete wrapperEl._flatpickr } catch (e) { wrapperEl._flatpickr = null }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default {
|
|
102
|
+
attachFlatpickr,
|
|
103
|
+
detachFlatpickr
|
|
104
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { html, css, formField, fieldIsTouched } from 'halfcab'
|
|
2
2
|
import flatpickr from 'flatpickr'
|
|
3
|
+
import { attachFlatpickr } from '../adapters/flatpickr.mjs'
|
|
3
4
|
import calendarIcon from './icons/calendarIcon.mjs'
|
|
4
5
|
import timeIcon from './icons/timeIcon.mjs'
|
|
5
6
|
|
|
@@ -105,6 +106,15 @@ function detectTouchscreen () {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
export default ({ wrapperStyle = null, holdingPen, label, placeholder, property, required, pattern, permanentTopPlaceholder = true, permanentTopLabel = false, flatpickrConfig = {}, timeOnly = false, disabled, disableClear = false, onchange, oninput }) => {
|
|
109
|
+
// Determine whether we should force Flatpickr on mobile/touch devices.
|
|
110
|
+
// By default we enable Flatpickr everywhere (disableMobile: true) so that
|
|
111
|
+
// environments that would otherwise show the native date/time inputs will
|
|
112
|
+
// still initialize the Flatpickr widget. Consumers can override by passing
|
|
113
|
+
// { disableMobile: false } inside flatpickrConfig.
|
|
114
|
+
const resolvedDisableMobile = flatpickrConfig && typeof flatpickrConfig.disableMobile !== 'undefined'
|
|
115
|
+
? flatpickrConfig.disableMobile
|
|
116
|
+
: true
|
|
117
|
+
|
|
108
118
|
let el = html`
|
|
109
119
|
<div ${wrapperStyle ? { 'class': wrapperStyle } : ''} style="min-height: 55px; display: inline-block; width: calc(100% - 10px); margin: 40px 5px 5px 5px;">
|
|
110
120
|
<div style="display: inline-block; width: 100%; text-align: left; position: relative; padding: 0;">
|
|
@@ -130,12 +140,16 @@ export default ({ wrapperStyle = null, holdingPen, label, placeholder, property,
|
|
|
130
140
|
formField(holdingPen, property)(fauxE)
|
|
131
141
|
onchange && onchange(fauxE)
|
|
132
142
|
})
|
|
133
|
-
}} onchange=${e => { change({ e, holdingPen, property, label: styles.label }); onchange && onchange(e) }} oninput=${e => { e.target.defaultValue = ''; oninput && oninput(e) }} placeholder="${placeholder || ''}${required ? ' *' : ''}" type="${detectTouchscreen() ? timeOnly ? 'time' : 'date' : 'text'}" ${pattern ? { pattern } : ''} value="${holdingPen[property] || ''}" data-input />
|
|
143
|
+
}} onchange=${e => { change({ e, holdingPen, property, label: styles.label }); onchange && onchange(e) }} oninput=${e => { e.target.defaultValue = ''; oninput && oninput(e) }} placeholder="${placeholder || ''}${required ? ' *' : ''}" type="${resolvedDisableMobile ? 'text' : (detectTouchscreen() ? (timeOnly ? 'time' : 'date') : 'text')}" ${pattern ? { pattern } : ''} value="${holdingPen[property] || ''}" data-input />
|
|
134
144
|
</div>
|
|
135
145
|
</div>
|
|
136
146
|
`
|
|
137
147
|
|
|
138
|
-
|
|
148
|
+
// Attach flatpickr instance to the OUTER WRAPPER element (not the fragment) so
|
|
149
|
+
// internal clear/close handlers can access it consistently via parent wrappers.
|
|
150
|
+
// Using `this` or the fragment caused errors after migrating to halfcab/lit.
|
|
151
|
+
const wrapper = el.firstElementChild || el
|
|
152
|
+
attachFlatpickr(wrapper, flatpickr, Object.assign({}, { disableMobile: resolvedDisableMobile }, flatpickrConfig, {
|
|
139
153
|
wrap: true,
|
|
140
154
|
onValueUpdate: (fpDate, dateString) => {
|
|
141
155
|
let fauxE = {
|