mithril-materialized 3.5.5 → 3.5.7
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/dist/index.esm.js +299 -118
- package/dist/index.js +302 -117
- package/dist/index.umd.js +302 -117
- package/dist/modal.d.ts +4 -2
- package/dist/timepicker.d.ts +1 -0
- package/dist/utils.d.ts +32 -0
- package/package.json +12 -13
package/dist/index.js
CHANGED
|
@@ -161,6 +161,76 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
|
|
|
161
161
|
* @example: console.log(range(5, 10)); // [5, 6, 7, 8, 9, 10]
|
|
162
162
|
*/
|
|
163
163
|
const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
|
164
|
+
// Global registry for portal containers
|
|
165
|
+
const portalContainers = new Map();
|
|
166
|
+
/**
|
|
167
|
+
* Creates or retrieves a portal container appended to document.body.
|
|
168
|
+
* Uses reference counting to manage container lifecycle.
|
|
169
|
+
*
|
|
170
|
+
* @param id - Unique identifier for the portal container
|
|
171
|
+
* @param zIndex - Z-index for the portal container (default: 1004, above modals at 1003)
|
|
172
|
+
* @returns The portal container element
|
|
173
|
+
*/
|
|
174
|
+
const getPortalContainer = (id, zIndex = 1004) => {
|
|
175
|
+
let container = portalContainers.get(id);
|
|
176
|
+
if (!container) {
|
|
177
|
+
const element = document.createElement('div');
|
|
178
|
+
element.id = id;
|
|
179
|
+
element.style.position = 'fixed';
|
|
180
|
+
element.style.top = '0';
|
|
181
|
+
element.style.left = '0';
|
|
182
|
+
element.style.width = '100%';
|
|
183
|
+
element.style.height = '100%';
|
|
184
|
+
element.style.pointerEvents = 'none'; // Allow clicks through to underlying elements
|
|
185
|
+
element.style.zIndex = zIndex.toString();
|
|
186
|
+
document.body.appendChild(element);
|
|
187
|
+
container = { element, refCount: 0 };
|
|
188
|
+
portalContainers.set(id, container);
|
|
189
|
+
}
|
|
190
|
+
container.refCount++;
|
|
191
|
+
return container.element;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Decrements reference count and removes portal container if no longer needed.
|
|
195
|
+
*
|
|
196
|
+
* @param id - Portal container identifier
|
|
197
|
+
*/
|
|
198
|
+
const releasePortalContainer = (id) => {
|
|
199
|
+
const container = portalContainers.get(id);
|
|
200
|
+
if (container) {
|
|
201
|
+
container.refCount--;
|
|
202
|
+
if (container.refCount <= 0) {
|
|
203
|
+
container.element.remove();
|
|
204
|
+
portalContainers.delete(id);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Renders a Mithril vnode into a portal container using m.render().
|
|
210
|
+
* This allows components to render outside their parent DOM hierarchy,
|
|
211
|
+
* useful for modals and pickers that need to escape stacking contexts.
|
|
212
|
+
*
|
|
213
|
+
* @param containerId - Portal container identifier
|
|
214
|
+
* @param vnode - Mithril vnode to render
|
|
215
|
+
* @param zIndex - Z-index for portal container (default: 1004)
|
|
216
|
+
*/
|
|
217
|
+
const renderToPortal = (containerId, vnode, zIndex = 1004) => {
|
|
218
|
+
const container = getPortalContainer(containerId, zIndex);
|
|
219
|
+
m.render(container, vnode);
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Clears portal content and releases container reference.
|
|
223
|
+
* If this is the last reference, the container will be removed from the DOM.
|
|
224
|
+
*
|
|
225
|
+
* @param containerId - Portal container identifier
|
|
226
|
+
*/
|
|
227
|
+
const clearPortal = (containerId) => {
|
|
228
|
+
const container = portalContainers.get(containerId);
|
|
229
|
+
if (container) {
|
|
230
|
+
m.render(container.element, null);
|
|
231
|
+
releasePortalContainer(containerId);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
164
234
|
|
|
165
235
|
// import './styles/input.css';
|
|
166
236
|
const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
|
|
@@ -2189,6 +2259,129 @@ const DatePicker = () => {
|
|
|
2189
2259
|
}
|
|
2190
2260
|
m.redraw();
|
|
2191
2261
|
};
|
|
2262
|
+
const handleKeyDown = (e) => {
|
|
2263
|
+
if (e.key === 'Escape' && state.isOpen) {
|
|
2264
|
+
state.isOpen = false;
|
|
2265
|
+
const options = mergeOptions({});
|
|
2266
|
+
if (options.onClose)
|
|
2267
|
+
options.onClose();
|
|
2268
|
+
clearPortal(state.portalContainerId);
|
|
2269
|
+
m.redraw();
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
const renderPickerToPortal = (attrs) => {
|
|
2273
|
+
const options = mergeOptions(attrs);
|
|
2274
|
+
const pickerModal = m('.datepicker-modal-wrapper', {
|
|
2275
|
+
style: {
|
|
2276
|
+
position: 'fixed',
|
|
2277
|
+
top: '0',
|
|
2278
|
+
left: '0',
|
|
2279
|
+
width: '100%',
|
|
2280
|
+
height: '100%',
|
|
2281
|
+
pointerEvents: 'auto',
|
|
2282
|
+
display: 'flex',
|
|
2283
|
+
alignItems: 'center',
|
|
2284
|
+
justifyContent: 'center',
|
|
2285
|
+
},
|
|
2286
|
+
}, [
|
|
2287
|
+
// Modal overlay
|
|
2288
|
+
m('.modal-overlay', {
|
|
2289
|
+
style: {
|
|
2290
|
+
position: 'absolute',
|
|
2291
|
+
top: '0',
|
|
2292
|
+
left: '0',
|
|
2293
|
+
width: '100%',
|
|
2294
|
+
height: '100%',
|
|
2295
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
2296
|
+
zIndex: '1002',
|
|
2297
|
+
},
|
|
2298
|
+
onclick: () => {
|
|
2299
|
+
state.isOpen = false;
|
|
2300
|
+
if (options.onClose)
|
|
2301
|
+
options.onClose();
|
|
2302
|
+
m.redraw();
|
|
2303
|
+
},
|
|
2304
|
+
}),
|
|
2305
|
+
// Modal content
|
|
2306
|
+
m('.modal.datepicker-modal.open', {
|
|
2307
|
+
id: `modal-${state.id}`,
|
|
2308
|
+
tabindex: 0,
|
|
2309
|
+
style: {
|
|
2310
|
+
position: 'relative',
|
|
2311
|
+
zIndex: '1003',
|
|
2312
|
+
display: 'block',
|
|
2313
|
+
opacity: 1,
|
|
2314
|
+
top: 'auto',
|
|
2315
|
+
transform: 'scaleX(1) scaleY(1)',
|
|
2316
|
+
margin: '0 auto',
|
|
2317
|
+
},
|
|
2318
|
+
}, [
|
|
2319
|
+
m('.modal-content.datepicker-container', {
|
|
2320
|
+
onclick: (e) => {
|
|
2321
|
+
const target = e.target;
|
|
2322
|
+
if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
|
|
2323
|
+
state.monthDropdownOpen = false;
|
|
2324
|
+
state.yearDropdownOpen = false;
|
|
2325
|
+
}
|
|
2326
|
+
},
|
|
2327
|
+
}, [
|
|
2328
|
+
m(DateDisplay, { options }),
|
|
2329
|
+
m('.datepicker-calendar-container', [
|
|
2330
|
+
m('.datepicker-calendar', [
|
|
2331
|
+
m(DateControls, {
|
|
2332
|
+
options,
|
|
2333
|
+
randId: `datepicker-title-${Math.random().toString(36).slice(2)}`,
|
|
2334
|
+
}),
|
|
2335
|
+
m(Calendar, {
|
|
2336
|
+
year: state.calendars[0].year,
|
|
2337
|
+
month: state.calendars[0].month,
|
|
2338
|
+
options,
|
|
2339
|
+
}),
|
|
2340
|
+
]),
|
|
2341
|
+
m('.datepicker-footer', [
|
|
2342
|
+
options.showClearBtn &&
|
|
2343
|
+
m('button.btn-flat.datepicker-clear.waves-effect', {
|
|
2344
|
+
type: 'button',
|
|
2345
|
+
onclick: () => {
|
|
2346
|
+
setDate(null, false, options);
|
|
2347
|
+
state.isOpen = false;
|
|
2348
|
+
},
|
|
2349
|
+
}, options.i18n.clear),
|
|
2350
|
+
m('button.btn-flat.datepicker-cancel.waves-effect', {
|
|
2351
|
+
type: 'button',
|
|
2352
|
+
onclick: () => {
|
|
2353
|
+
state.isOpen = false;
|
|
2354
|
+
if (options.onClose)
|
|
2355
|
+
options.onClose();
|
|
2356
|
+
},
|
|
2357
|
+
}, options.i18n.cancel),
|
|
2358
|
+
m('button.btn-flat.datepicker-done.waves-effect', {
|
|
2359
|
+
type: 'button',
|
|
2360
|
+
onclick: () => {
|
|
2361
|
+
state.isOpen = false;
|
|
2362
|
+
if (options.dateRange) {
|
|
2363
|
+
if (state.startDate && state.endDate && attrs.onchange) {
|
|
2364
|
+
const startStr = formatDate(state.startDate, 'yyyy-mm-dd', options);
|
|
2365
|
+
const endStr = formatDate(state.endDate, 'yyyy-mm-dd', options);
|
|
2366
|
+
attrs.onchange(`${startStr} - ${endStr}`);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
else {
|
|
2370
|
+
if (state.date && attrs.onchange) {
|
|
2371
|
+
attrs.onchange(toString(state.date, 'yyyy-mm-dd'));
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
if (options.onClose)
|
|
2375
|
+
options.onClose();
|
|
2376
|
+
},
|
|
2377
|
+
}, options.i18n.done),
|
|
2378
|
+
]),
|
|
2379
|
+
]),
|
|
2380
|
+
]),
|
|
2381
|
+
]),
|
|
2382
|
+
]);
|
|
2383
|
+
renderToPortal(state.portalContainerId, pickerModal, 1004);
|
|
2384
|
+
};
|
|
2192
2385
|
return {
|
|
2193
2386
|
oninit: (vnode) => {
|
|
2194
2387
|
const attrs = vnode.attrs;
|
|
@@ -2204,6 +2397,7 @@ const DatePicker = () => {
|
|
|
2204
2397
|
calendars: [{ month: 0, year: 0 }],
|
|
2205
2398
|
monthDropdownOpen: false,
|
|
2206
2399
|
yearDropdownOpen: false,
|
|
2400
|
+
portalContainerId: `datepicker-portal-${uniqueId()}`,
|
|
2207
2401
|
formats: {
|
|
2208
2402
|
d: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDate()) || 0; },
|
|
2209
2403
|
dd: () => {
|
|
@@ -2257,10 +2451,26 @@ const DatePicker = () => {
|
|
|
2257
2451
|
}
|
|
2258
2452
|
// Add document click listener to close dropdowns
|
|
2259
2453
|
document.addEventListener('click', handleDocumentClick);
|
|
2454
|
+
// Add ESC key listener
|
|
2455
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
2260
2456
|
},
|
|
2261
2457
|
onremove: () => {
|
|
2262
|
-
// Clean up event
|
|
2458
|
+
// Clean up event listeners
|
|
2263
2459
|
document.removeEventListener('click', handleDocumentClick);
|
|
2460
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
2461
|
+
// Clean up portal if picker was open
|
|
2462
|
+
if (state.isOpen) {
|
|
2463
|
+
clearPortal(state.portalContainerId);
|
|
2464
|
+
}
|
|
2465
|
+
},
|
|
2466
|
+
onupdate: (vnode) => {
|
|
2467
|
+
// Render to portal when picker is open, clear when closed
|
|
2468
|
+
if (state.isOpen) {
|
|
2469
|
+
renderPickerToPortal(vnode.attrs);
|
|
2470
|
+
}
|
|
2471
|
+
else {
|
|
2472
|
+
clearPortal(state.portalContainerId);
|
|
2473
|
+
}
|
|
2264
2474
|
},
|
|
2265
2475
|
view: (vnode) => {
|
|
2266
2476
|
const attrs = vnode.attrs;
|
|
@@ -2376,93 +2586,7 @@ const DatePicker = () => {
|
|
|
2376
2586
|
}, label || dateLabel),
|
|
2377
2587
|
// Helper text
|
|
2378
2588
|
helperText && m('span.helper-text', helperText),
|
|
2379
|
-
// Modal
|
|
2380
|
-
state.isOpen && [
|
|
2381
|
-
m('.modal.datepicker-modal.open', {
|
|
2382
|
-
id: `modal-${state.id}`,
|
|
2383
|
-
tabindex: 0,
|
|
2384
|
-
style: {
|
|
2385
|
-
zIndex: 1003,
|
|
2386
|
-
display: 'block',
|
|
2387
|
-
opacity: 1,
|
|
2388
|
-
top: '10%',
|
|
2389
|
-
transform: 'scaleX(1) scaleY(1)',
|
|
2390
|
-
},
|
|
2391
|
-
}, [
|
|
2392
|
-
m('.modal-content.datepicker-container', {
|
|
2393
|
-
onclick: (e) => {
|
|
2394
|
-
// Close dropdowns when clicking anywhere in the modal content
|
|
2395
|
-
const target = e.target;
|
|
2396
|
-
if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
|
|
2397
|
-
state.monthDropdownOpen = false;
|
|
2398
|
-
state.yearDropdownOpen = false;
|
|
2399
|
-
}
|
|
2400
|
-
},
|
|
2401
|
-
}, [
|
|
2402
|
-
m(DateDisplay, { options }),
|
|
2403
|
-
m('.datepicker-calendar-container', [
|
|
2404
|
-
m('.datepicker-calendar', [
|
|
2405
|
-
m(DateControls, { options, randId: `datepicker-title-${Math.random().toString(36).slice(2)}` }),
|
|
2406
|
-
m(Calendar, { year: state.calendars[0].year, month: state.calendars[0].month, options }),
|
|
2407
|
-
]),
|
|
2408
|
-
m('.datepicker-footer', [
|
|
2409
|
-
options.showClearBtn &&
|
|
2410
|
-
m('button.btn-flat.datepicker-clear.waves-effect', {
|
|
2411
|
-
type: 'button',
|
|
2412
|
-
style: '',
|
|
2413
|
-
onclick: () => {
|
|
2414
|
-
setDate(null, false, options);
|
|
2415
|
-
state.isOpen = false;
|
|
2416
|
-
},
|
|
2417
|
-
}, options.i18n.clear),
|
|
2418
|
-
m('button.btn-flat.datepicker-cancel.waves-effect', {
|
|
2419
|
-
type: 'button',
|
|
2420
|
-
onclick: () => {
|
|
2421
|
-
state.isOpen = false;
|
|
2422
|
-
if (options.onClose)
|
|
2423
|
-
options.onClose();
|
|
2424
|
-
},
|
|
2425
|
-
}, options.i18n.cancel),
|
|
2426
|
-
m('button.btn-flat.datepicker-done.waves-effect', {
|
|
2427
|
-
type: 'button',
|
|
2428
|
-
onclick: () => {
|
|
2429
|
-
state.isOpen = false;
|
|
2430
|
-
if (options.dateRange) {
|
|
2431
|
-
// Range mode
|
|
2432
|
-
if (state.startDate && state.endDate && onchange) {
|
|
2433
|
-
const startStr = toString(state.startDate, 'yyyy-mm-dd');
|
|
2434
|
-
const endStr = toString(state.endDate, 'yyyy-mm-dd');
|
|
2435
|
-
onchange(`${startStr} - ${endStr}`);
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
else {
|
|
2439
|
-
// Single date mode
|
|
2440
|
-
if (state.date && onchange) {
|
|
2441
|
-
onchange(toString(state.date, 'yyyy-mm-dd')); // Always return ISO format
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
if (options.onClose)
|
|
2445
|
-
options.onClose();
|
|
2446
|
-
},
|
|
2447
|
-
}, options.i18n.done),
|
|
2448
|
-
]),
|
|
2449
|
-
]),
|
|
2450
|
-
]),
|
|
2451
|
-
]),
|
|
2452
|
-
// Modal overlay
|
|
2453
|
-
m('.modal-overlay', {
|
|
2454
|
-
style: {
|
|
2455
|
-
zIndex: 1002,
|
|
2456
|
-
display: 'block',
|
|
2457
|
-
opacity: 0.5,
|
|
2458
|
-
},
|
|
2459
|
-
onclick: () => {
|
|
2460
|
-
state.isOpen = false;
|
|
2461
|
-
if (options.onClose)
|
|
2462
|
-
options.onClose();
|
|
2463
|
-
},
|
|
2464
|
-
}),
|
|
2465
|
-
],
|
|
2589
|
+
// Modal is now rendered via portal in onupdate hook
|
|
2466
2590
|
]);
|
|
2467
2591
|
},
|
|
2468
2592
|
};
|
|
@@ -3302,8 +3426,8 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3302
3426
|
const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
|
|
3303
3427
|
return m('.input-field', { className: cn, style }, [
|
|
3304
3428
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
3305
|
-
m('input', Object.assign(Object.assign({ class: shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
|
|
3306
|
-
placeholder, value: controlled ? value : undefined,
|
|
3429
|
+
m('input', Object.assign(Object.assign({ class: type === 'range' && attrs.vertical ? 'range-slider vertical' : shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
|
|
3430
|
+
placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
|
|
3307
3431
|
? {
|
|
3308
3432
|
height: attrs.height || '200px',
|
|
3309
3433
|
width: '6px',
|
|
@@ -4896,7 +5020,7 @@ const ModalPanel = () => {
|
|
|
4896
5020
|
closeModal(attrs);
|
|
4897
5021
|
}
|
|
4898
5022
|
}
|
|
4899
|
-
const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, } = attrs;
|
|
5023
|
+
const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, closeOnButtonClick = true, } = attrs;
|
|
4900
5024
|
const modalClasses = [
|
|
4901
5025
|
'modal',
|
|
4902
5026
|
className || '',
|
|
@@ -4993,7 +5117,7 @@ const ModalPanel = () => {
|
|
|
4993
5117
|
}, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
|
|
4994
5118
|
if (buttonProps.onclick)
|
|
4995
5119
|
buttonProps.onclick(e);
|
|
4996
|
-
closeModal(attrs);
|
|
5120
|
+
closeOnButtonClick && closeModal(attrs);
|
|
4997
5121
|
} })))),
|
|
4998
5122
|
]),
|
|
4999
5123
|
]);
|
|
@@ -5171,6 +5295,7 @@ const defaultOptions = {
|
|
|
5171
5295
|
autoClose: false,
|
|
5172
5296
|
twelveHour: true,
|
|
5173
5297
|
vibrate: true,
|
|
5298
|
+
roundBy5: false,
|
|
5174
5299
|
onOpen: () => { },
|
|
5175
5300
|
onOpenStart: () => { },
|
|
5176
5301
|
onOpenEnd: () => { },
|
|
@@ -5222,7 +5347,7 @@ const TimePicker = () => {
|
|
|
5222
5347
|
const clickPos = getPos(e);
|
|
5223
5348
|
state.dx = clickPos.x - state.x0;
|
|
5224
5349
|
state.dy = clickPos.y - state.y0;
|
|
5225
|
-
setHand(state.dx, state.dy,
|
|
5350
|
+
setHand(state.dx, state.dy, options.roundBy5);
|
|
5226
5351
|
document.addEventListener('mousemove', handleDocumentClickMove);
|
|
5227
5352
|
document.addEventListener('touchmove', handleDocumentClickMove);
|
|
5228
5353
|
document.addEventListener('mouseup', handleDocumentClickEnd);
|
|
@@ -5234,7 +5359,7 @@ const TimePicker = () => {
|
|
|
5234
5359
|
const x = clickPos.x - state.x0;
|
|
5235
5360
|
const y = clickPos.y - state.y0;
|
|
5236
5361
|
state.moved = true;
|
|
5237
|
-
setHand(x, y,
|
|
5362
|
+
setHand(x, y, options.roundBy5);
|
|
5238
5363
|
m.redraw();
|
|
5239
5364
|
};
|
|
5240
5365
|
const handleDocumentClickEnd = (e) => {
|
|
@@ -5670,6 +5795,66 @@ const TimePicker = () => {
|
|
|
5670
5795
|
},
|
|
5671
5796
|
};
|
|
5672
5797
|
};
|
|
5798
|
+
const handleKeyDown = (e) => {
|
|
5799
|
+
if (e.key === 'Escape' && state.isOpen) {
|
|
5800
|
+
close();
|
|
5801
|
+
clearPortal(state.portalContainerId);
|
|
5802
|
+
m.redraw();
|
|
5803
|
+
}
|
|
5804
|
+
};
|
|
5805
|
+
const renderPickerToPortal = (attrs) => {
|
|
5806
|
+
const { showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel' } = attrs;
|
|
5807
|
+
const pickerModal = m('.timepicker-modal-wrapper', {
|
|
5808
|
+
style: {
|
|
5809
|
+
position: 'fixed',
|
|
5810
|
+
top: '0',
|
|
5811
|
+
left: '0',
|
|
5812
|
+
width: '100%',
|
|
5813
|
+
height: '100%',
|
|
5814
|
+
pointerEvents: 'auto',
|
|
5815
|
+
display: 'flex',
|
|
5816
|
+
alignItems: 'center',
|
|
5817
|
+
justifyContent: 'center',
|
|
5818
|
+
},
|
|
5819
|
+
}, [
|
|
5820
|
+
// Modal overlay
|
|
5821
|
+
m('.modal-overlay', {
|
|
5822
|
+
style: {
|
|
5823
|
+
position: 'absolute',
|
|
5824
|
+
top: '0',
|
|
5825
|
+
left: '0',
|
|
5826
|
+
width: '100%',
|
|
5827
|
+
height: '100%',
|
|
5828
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
5829
|
+
zIndex: '1002',
|
|
5830
|
+
},
|
|
5831
|
+
onclick: () => {
|
|
5832
|
+
close();
|
|
5833
|
+
m.redraw();
|
|
5834
|
+
},
|
|
5835
|
+
}),
|
|
5836
|
+
// Modal content
|
|
5837
|
+
m('.modal.timepicker-modal.open', {
|
|
5838
|
+
style: {
|
|
5839
|
+
position: 'relative',
|
|
5840
|
+
zIndex: '1003',
|
|
5841
|
+
display: 'block',
|
|
5842
|
+
opacity: 1,
|
|
5843
|
+
top: 'auto',
|
|
5844
|
+
transform: 'scaleX(1) scaleY(1)',
|
|
5845
|
+
margin: '0 auto',
|
|
5846
|
+
},
|
|
5847
|
+
}, [
|
|
5848
|
+
m(TimepickerModal, {
|
|
5849
|
+
showClearBtn,
|
|
5850
|
+
clearLabel,
|
|
5851
|
+
closeLabel,
|
|
5852
|
+
doneLabel: 'OK',
|
|
5853
|
+
}),
|
|
5854
|
+
]),
|
|
5855
|
+
]);
|
|
5856
|
+
renderToPortal(state.portalContainerId, pickerModal, 1004);
|
|
5857
|
+
};
|
|
5673
5858
|
return {
|
|
5674
5859
|
oninit: (vnode) => {
|
|
5675
5860
|
const attrs = vnode.attrs;
|
|
@@ -5686,11 +5871,14 @@ const TimePicker = () => {
|
|
|
5686
5871
|
y0: 0,
|
|
5687
5872
|
dx: 0,
|
|
5688
5873
|
dy: 0,
|
|
5874
|
+
portalContainerId: `timepicker-portal-${uniqueId()}`,
|
|
5689
5875
|
};
|
|
5690
5876
|
// Handle value after options are set
|
|
5691
5877
|
if (attrs.defaultValue) {
|
|
5692
5878
|
updateTimeFromInput(attrs.defaultValue);
|
|
5693
5879
|
}
|
|
5880
|
+
// Add ESC key listener
|
|
5881
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
5694
5882
|
},
|
|
5695
5883
|
onremove: () => {
|
|
5696
5884
|
// Cleanup
|
|
@@ -5704,6 +5892,21 @@ const TimePicker = () => {
|
|
|
5704
5892
|
document.removeEventListener('touchmove', handleDocumentClickMove);
|
|
5705
5893
|
document.removeEventListener('mouseup', handleDocumentClickEnd);
|
|
5706
5894
|
document.removeEventListener('touchend', handleDocumentClickEnd);
|
|
5895
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
5896
|
+
// Clean up portal if picker was open
|
|
5897
|
+
if (state.isOpen) {
|
|
5898
|
+
clearPortal(state.portalContainerId);
|
|
5899
|
+
}
|
|
5900
|
+
},
|
|
5901
|
+
onupdate: (vnode) => {
|
|
5902
|
+
const { useModal = true } = vnode.attrs;
|
|
5903
|
+
// Only render to portal when using modal mode
|
|
5904
|
+
if (useModal && state.isOpen) {
|
|
5905
|
+
renderPickerToPortal(vnode.attrs);
|
|
5906
|
+
}
|
|
5907
|
+
else {
|
|
5908
|
+
clearPortal(state.portalContainerId);
|
|
5909
|
+
}
|
|
5707
5910
|
},
|
|
5708
5911
|
view: ({ attrs }) => {
|
|
5709
5912
|
const { id = state.id, label, placeholder, disabled, readonly, required, iconName, helperText, onchange, oninput, useModal = true, showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel', twelveHour, className: cn1, class: cn2, } = attrs;
|
|
@@ -5800,29 +6003,7 @@ const TimePicker = () => {
|
|
|
5800
6003
|
}, label),
|
|
5801
6004
|
// Helper text
|
|
5802
6005
|
helperText && m('span.helper-text', helperText),
|
|
5803
|
-
// Modal
|
|
5804
|
-
useModal &&
|
|
5805
|
-
state.isOpen && [
|
|
5806
|
-
// Modal overlay
|
|
5807
|
-
m('.modal-overlay', {
|
|
5808
|
-
style: {
|
|
5809
|
-
zIndex: 1002,
|
|
5810
|
-
display: 'block',
|
|
5811
|
-
opacity: 0.5,
|
|
5812
|
-
},
|
|
5813
|
-
onclick: () => close(),
|
|
5814
|
-
}),
|
|
5815
|
-
// Modal content
|
|
5816
|
-
m('.modal.timepicker-modal.open', {
|
|
5817
|
-
style: {
|
|
5818
|
-
zIndex: 1003,
|
|
5819
|
-
display: 'block',
|
|
5820
|
-
opacity: 1,
|
|
5821
|
-
top: '10%',
|
|
5822
|
-
transform: 'scaleX(1) scaleY(1)',
|
|
5823
|
-
},
|
|
5824
|
-
}, m(TimepickerModal, { showClearBtn, clearLabel, closeLabel, doneLabel: 'OK' })),
|
|
5825
|
-
],
|
|
6006
|
+
// Modal is now rendered via portal in onupdate hook
|
|
5826
6007
|
]);
|
|
5827
6008
|
},
|
|
5828
6009
|
};
|
|
@@ -9621,8 +9802,10 @@ exports.TooltipComponent = TooltipComponent;
|
|
|
9621
9802
|
exports.TreeView = TreeView;
|
|
9622
9803
|
exports.UrlInput = UrlInput;
|
|
9623
9804
|
exports.Wizard = Wizard;
|
|
9805
|
+
exports.clearPortal = clearPortal;
|
|
9624
9806
|
exports.createBreadcrumb = createBreadcrumb;
|
|
9625
9807
|
exports.getDropdownStyles = getDropdownStyles;
|
|
9808
|
+
exports.getPortalContainer = getPortalContainer;
|
|
9626
9809
|
exports.initPushpins = initPushpins;
|
|
9627
9810
|
exports.initTooltips = initTooltips;
|
|
9628
9811
|
exports.isNumeric = isNumeric;
|
|
@@ -9630,6 +9813,8 @@ exports.isValidationError = isValidationError;
|
|
|
9630
9813
|
exports.isValidationSuccess = isValidationSuccess;
|
|
9631
9814
|
exports.padLeft = padLeft;
|
|
9632
9815
|
exports.range = range;
|
|
9816
|
+
exports.releasePortalContainer = releasePortalContainer;
|
|
9817
|
+
exports.renderToPortal = renderToPortal;
|
|
9633
9818
|
exports.toast = toast;
|
|
9634
9819
|
exports.uniqueId = uniqueId;
|
|
9635
9820
|
exports.uuid4 = uuid4;
|