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.esm.js
CHANGED
|
@@ -159,6 +159,76 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
|
|
|
159
159
|
* @example: console.log(range(5, 10)); // [5, 6, 7, 8, 9, 10]
|
|
160
160
|
*/
|
|
161
161
|
const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
|
162
|
+
// Global registry for portal containers
|
|
163
|
+
const portalContainers = new Map();
|
|
164
|
+
/**
|
|
165
|
+
* Creates or retrieves a portal container appended to document.body.
|
|
166
|
+
* Uses reference counting to manage container lifecycle.
|
|
167
|
+
*
|
|
168
|
+
* @param id - Unique identifier for the portal container
|
|
169
|
+
* @param zIndex - Z-index for the portal container (default: 1004, above modals at 1003)
|
|
170
|
+
* @returns The portal container element
|
|
171
|
+
*/
|
|
172
|
+
const getPortalContainer = (id, zIndex = 1004) => {
|
|
173
|
+
let container = portalContainers.get(id);
|
|
174
|
+
if (!container) {
|
|
175
|
+
const element = document.createElement('div');
|
|
176
|
+
element.id = id;
|
|
177
|
+
element.style.position = 'fixed';
|
|
178
|
+
element.style.top = '0';
|
|
179
|
+
element.style.left = '0';
|
|
180
|
+
element.style.width = '100%';
|
|
181
|
+
element.style.height = '100%';
|
|
182
|
+
element.style.pointerEvents = 'none'; // Allow clicks through to underlying elements
|
|
183
|
+
element.style.zIndex = zIndex.toString();
|
|
184
|
+
document.body.appendChild(element);
|
|
185
|
+
container = { element, refCount: 0 };
|
|
186
|
+
portalContainers.set(id, container);
|
|
187
|
+
}
|
|
188
|
+
container.refCount++;
|
|
189
|
+
return container.element;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Decrements reference count and removes portal container if no longer needed.
|
|
193
|
+
*
|
|
194
|
+
* @param id - Portal container identifier
|
|
195
|
+
*/
|
|
196
|
+
const releasePortalContainer = (id) => {
|
|
197
|
+
const container = portalContainers.get(id);
|
|
198
|
+
if (container) {
|
|
199
|
+
container.refCount--;
|
|
200
|
+
if (container.refCount <= 0) {
|
|
201
|
+
container.element.remove();
|
|
202
|
+
portalContainers.delete(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Renders a Mithril vnode into a portal container using m.render().
|
|
208
|
+
* This allows components to render outside their parent DOM hierarchy,
|
|
209
|
+
* useful for modals and pickers that need to escape stacking contexts.
|
|
210
|
+
*
|
|
211
|
+
* @param containerId - Portal container identifier
|
|
212
|
+
* @param vnode - Mithril vnode to render
|
|
213
|
+
* @param zIndex - Z-index for portal container (default: 1004)
|
|
214
|
+
*/
|
|
215
|
+
const renderToPortal = (containerId, vnode, zIndex = 1004) => {
|
|
216
|
+
const container = getPortalContainer(containerId, zIndex);
|
|
217
|
+
m.render(container, vnode);
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Clears portal content and releases container reference.
|
|
221
|
+
* If this is the last reference, the container will be removed from the DOM.
|
|
222
|
+
*
|
|
223
|
+
* @param containerId - Portal container identifier
|
|
224
|
+
*/
|
|
225
|
+
const clearPortal = (containerId) => {
|
|
226
|
+
const container = portalContainers.get(containerId);
|
|
227
|
+
if (container) {
|
|
228
|
+
m.render(container.element, null);
|
|
229
|
+
releasePortalContainer(containerId);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
162
232
|
|
|
163
233
|
// import './styles/input.css';
|
|
164
234
|
const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
|
|
@@ -2187,6 +2257,129 @@ const DatePicker = () => {
|
|
|
2187
2257
|
}
|
|
2188
2258
|
m.redraw();
|
|
2189
2259
|
};
|
|
2260
|
+
const handleKeyDown = (e) => {
|
|
2261
|
+
if (e.key === 'Escape' && state.isOpen) {
|
|
2262
|
+
state.isOpen = false;
|
|
2263
|
+
const options = mergeOptions({});
|
|
2264
|
+
if (options.onClose)
|
|
2265
|
+
options.onClose();
|
|
2266
|
+
clearPortal(state.portalContainerId);
|
|
2267
|
+
m.redraw();
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
const renderPickerToPortal = (attrs) => {
|
|
2271
|
+
const options = mergeOptions(attrs);
|
|
2272
|
+
const pickerModal = m('.datepicker-modal-wrapper', {
|
|
2273
|
+
style: {
|
|
2274
|
+
position: 'fixed',
|
|
2275
|
+
top: '0',
|
|
2276
|
+
left: '0',
|
|
2277
|
+
width: '100%',
|
|
2278
|
+
height: '100%',
|
|
2279
|
+
pointerEvents: 'auto',
|
|
2280
|
+
display: 'flex',
|
|
2281
|
+
alignItems: 'center',
|
|
2282
|
+
justifyContent: 'center',
|
|
2283
|
+
},
|
|
2284
|
+
}, [
|
|
2285
|
+
// Modal overlay
|
|
2286
|
+
m('.modal-overlay', {
|
|
2287
|
+
style: {
|
|
2288
|
+
position: 'absolute',
|
|
2289
|
+
top: '0',
|
|
2290
|
+
left: '0',
|
|
2291
|
+
width: '100%',
|
|
2292
|
+
height: '100%',
|
|
2293
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
2294
|
+
zIndex: '1002',
|
|
2295
|
+
},
|
|
2296
|
+
onclick: () => {
|
|
2297
|
+
state.isOpen = false;
|
|
2298
|
+
if (options.onClose)
|
|
2299
|
+
options.onClose();
|
|
2300
|
+
m.redraw();
|
|
2301
|
+
},
|
|
2302
|
+
}),
|
|
2303
|
+
// Modal content
|
|
2304
|
+
m('.modal.datepicker-modal.open', {
|
|
2305
|
+
id: `modal-${state.id}`,
|
|
2306
|
+
tabindex: 0,
|
|
2307
|
+
style: {
|
|
2308
|
+
position: 'relative',
|
|
2309
|
+
zIndex: '1003',
|
|
2310
|
+
display: 'block',
|
|
2311
|
+
opacity: 1,
|
|
2312
|
+
top: 'auto',
|
|
2313
|
+
transform: 'scaleX(1) scaleY(1)',
|
|
2314
|
+
margin: '0 auto',
|
|
2315
|
+
},
|
|
2316
|
+
}, [
|
|
2317
|
+
m('.modal-content.datepicker-container', {
|
|
2318
|
+
onclick: (e) => {
|
|
2319
|
+
const target = e.target;
|
|
2320
|
+
if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
|
|
2321
|
+
state.monthDropdownOpen = false;
|
|
2322
|
+
state.yearDropdownOpen = false;
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2325
|
+
}, [
|
|
2326
|
+
m(DateDisplay, { options }),
|
|
2327
|
+
m('.datepicker-calendar-container', [
|
|
2328
|
+
m('.datepicker-calendar', [
|
|
2329
|
+
m(DateControls, {
|
|
2330
|
+
options,
|
|
2331
|
+
randId: `datepicker-title-${Math.random().toString(36).slice(2)}`,
|
|
2332
|
+
}),
|
|
2333
|
+
m(Calendar, {
|
|
2334
|
+
year: state.calendars[0].year,
|
|
2335
|
+
month: state.calendars[0].month,
|
|
2336
|
+
options,
|
|
2337
|
+
}),
|
|
2338
|
+
]),
|
|
2339
|
+
m('.datepicker-footer', [
|
|
2340
|
+
options.showClearBtn &&
|
|
2341
|
+
m('button.btn-flat.datepicker-clear.waves-effect', {
|
|
2342
|
+
type: 'button',
|
|
2343
|
+
onclick: () => {
|
|
2344
|
+
setDate(null, false, options);
|
|
2345
|
+
state.isOpen = false;
|
|
2346
|
+
},
|
|
2347
|
+
}, options.i18n.clear),
|
|
2348
|
+
m('button.btn-flat.datepicker-cancel.waves-effect', {
|
|
2349
|
+
type: 'button',
|
|
2350
|
+
onclick: () => {
|
|
2351
|
+
state.isOpen = false;
|
|
2352
|
+
if (options.onClose)
|
|
2353
|
+
options.onClose();
|
|
2354
|
+
},
|
|
2355
|
+
}, options.i18n.cancel),
|
|
2356
|
+
m('button.btn-flat.datepicker-done.waves-effect', {
|
|
2357
|
+
type: 'button',
|
|
2358
|
+
onclick: () => {
|
|
2359
|
+
state.isOpen = false;
|
|
2360
|
+
if (options.dateRange) {
|
|
2361
|
+
if (state.startDate && state.endDate && attrs.onchange) {
|
|
2362
|
+
const startStr = formatDate(state.startDate, 'yyyy-mm-dd', options);
|
|
2363
|
+
const endStr = formatDate(state.endDate, 'yyyy-mm-dd', options);
|
|
2364
|
+
attrs.onchange(`${startStr} - ${endStr}`);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
else {
|
|
2368
|
+
if (state.date && attrs.onchange) {
|
|
2369
|
+
attrs.onchange(toString(state.date, 'yyyy-mm-dd'));
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
if (options.onClose)
|
|
2373
|
+
options.onClose();
|
|
2374
|
+
},
|
|
2375
|
+
}, options.i18n.done),
|
|
2376
|
+
]),
|
|
2377
|
+
]),
|
|
2378
|
+
]),
|
|
2379
|
+
]),
|
|
2380
|
+
]);
|
|
2381
|
+
renderToPortal(state.portalContainerId, pickerModal, 1004);
|
|
2382
|
+
};
|
|
2190
2383
|
return {
|
|
2191
2384
|
oninit: (vnode) => {
|
|
2192
2385
|
const attrs = vnode.attrs;
|
|
@@ -2202,6 +2395,7 @@ const DatePicker = () => {
|
|
|
2202
2395
|
calendars: [{ month: 0, year: 0 }],
|
|
2203
2396
|
monthDropdownOpen: false,
|
|
2204
2397
|
yearDropdownOpen: false,
|
|
2398
|
+
portalContainerId: `datepicker-portal-${uniqueId()}`,
|
|
2205
2399
|
formats: {
|
|
2206
2400
|
d: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDate()) || 0; },
|
|
2207
2401
|
dd: () => {
|
|
@@ -2255,10 +2449,26 @@ const DatePicker = () => {
|
|
|
2255
2449
|
}
|
|
2256
2450
|
// Add document click listener to close dropdowns
|
|
2257
2451
|
document.addEventListener('click', handleDocumentClick);
|
|
2452
|
+
// Add ESC key listener
|
|
2453
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
2258
2454
|
},
|
|
2259
2455
|
onremove: () => {
|
|
2260
|
-
// Clean up event
|
|
2456
|
+
// Clean up event listeners
|
|
2261
2457
|
document.removeEventListener('click', handleDocumentClick);
|
|
2458
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
2459
|
+
// Clean up portal if picker was open
|
|
2460
|
+
if (state.isOpen) {
|
|
2461
|
+
clearPortal(state.portalContainerId);
|
|
2462
|
+
}
|
|
2463
|
+
},
|
|
2464
|
+
onupdate: (vnode) => {
|
|
2465
|
+
// Render to portal when picker is open, clear when closed
|
|
2466
|
+
if (state.isOpen) {
|
|
2467
|
+
renderPickerToPortal(vnode.attrs);
|
|
2468
|
+
}
|
|
2469
|
+
else {
|
|
2470
|
+
clearPortal(state.portalContainerId);
|
|
2471
|
+
}
|
|
2262
2472
|
},
|
|
2263
2473
|
view: (vnode) => {
|
|
2264
2474
|
const attrs = vnode.attrs;
|
|
@@ -2374,93 +2584,7 @@ const DatePicker = () => {
|
|
|
2374
2584
|
}, label || dateLabel),
|
|
2375
2585
|
// Helper text
|
|
2376
2586
|
helperText && m('span.helper-text', helperText),
|
|
2377
|
-
// Modal
|
|
2378
|
-
state.isOpen && [
|
|
2379
|
-
m('.modal.datepicker-modal.open', {
|
|
2380
|
-
id: `modal-${state.id}`,
|
|
2381
|
-
tabindex: 0,
|
|
2382
|
-
style: {
|
|
2383
|
-
zIndex: 1003,
|
|
2384
|
-
display: 'block',
|
|
2385
|
-
opacity: 1,
|
|
2386
|
-
top: '10%',
|
|
2387
|
-
transform: 'scaleX(1) scaleY(1)',
|
|
2388
|
-
},
|
|
2389
|
-
}, [
|
|
2390
|
-
m('.modal-content.datepicker-container', {
|
|
2391
|
-
onclick: (e) => {
|
|
2392
|
-
// Close dropdowns when clicking anywhere in the modal content
|
|
2393
|
-
const target = e.target;
|
|
2394
|
-
if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
|
|
2395
|
-
state.monthDropdownOpen = false;
|
|
2396
|
-
state.yearDropdownOpen = false;
|
|
2397
|
-
}
|
|
2398
|
-
},
|
|
2399
|
-
}, [
|
|
2400
|
-
m(DateDisplay, { options }),
|
|
2401
|
-
m('.datepicker-calendar-container', [
|
|
2402
|
-
m('.datepicker-calendar', [
|
|
2403
|
-
m(DateControls, { options, randId: `datepicker-title-${Math.random().toString(36).slice(2)}` }),
|
|
2404
|
-
m(Calendar, { year: state.calendars[0].year, month: state.calendars[0].month, options }),
|
|
2405
|
-
]),
|
|
2406
|
-
m('.datepicker-footer', [
|
|
2407
|
-
options.showClearBtn &&
|
|
2408
|
-
m('button.btn-flat.datepicker-clear.waves-effect', {
|
|
2409
|
-
type: 'button',
|
|
2410
|
-
style: '',
|
|
2411
|
-
onclick: () => {
|
|
2412
|
-
setDate(null, false, options);
|
|
2413
|
-
state.isOpen = false;
|
|
2414
|
-
},
|
|
2415
|
-
}, options.i18n.clear),
|
|
2416
|
-
m('button.btn-flat.datepicker-cancel.waves-effect', {
|
|
2417
|
-
type: 'button',
|
|
2418
|
-
onclick: () => {
|
|
2419
|
-
state.isOpen = false;
|
|
2420
|
-
if (options.onClose)
|
|
2421
|
-
options.onClose();
|
|
2422
|
-
},
|
|
2423
|
-
}, options.i18n.cancel),
|
|
2424
|
-
m('button.btn-flat.datepicker-done.waves-effect', {
|
|
2425
|
-
type: 'button',
|
|
2426
|
-
onclick: () => {
|
|
2427
|
-
state.isOpen = false;
|
|
2428
|
-
if (options.dateRange) {
|
|
2429
|
-
// Range mode
|
|
2430
|
-
if (state.startDate && state.endDate && onchange) {
|
|
2431
|
-
const startStr = toString(state.startDate, 'yyyy-mm-dd');
|
|
2432
|
-
const endStr = toString(state.endDate, 'yyyy-mm-dd');
|
|
2433
|
-
onchange(`${startStr} - ${endStr}`);
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
else {
|
|
2437
|
-
// Single date mode
|
|
2438
|
-
if (state.date && onchange) {
|
|
2439
|
-
onchange(toString(state.date, 'yyyy-mm-dd')); // Always return ISO format
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
if (options.onClose)
|
|
2443
|
-
options.onClose();
|
|
2444
|
-
},
|
|
2445
|
-
}, options.i18n.done),
|
|
2446
|
-
]),
|
|
2447
|
-
]),
|
|
2448
|
-
]),
|
|
2449
|
-
]),
|
|
2450
|
-
// Modal overlay
|
|
2451
|
-
m('.modal-overlay', {
|
|
2452
|
-
style: {
|
|
2453
|
-
zIndex: 1002,
|
|
2454
|
-
display: 'block',
|
|
2455
|
-
opacity: 0.5,
|
|
2456
|
-
},
|
|
2457
|
-
onclick: () => {
|
|
2458
|
-
state.isOpen = false;
|
|
2459
|
-
if (options.onClose)
|
|
2460
|
-
options.onClose();
|
|
2461
|
-
},
|
|
2462
|
-
}),
|
|
2463
|
-
],
|
|
2587
|
+
// Modal is now rendered via portal in onupdate hook
|
|
2464
2588
|
]);
|
|
2465
2589
|
},
|
|
2466
2590
|
};
|
|
@@ -3300,8 +3424,8 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3300
3424
|
const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
|
|
3301
3425
|
return m('.input-field', { className: cn, style }, [
|
|
3302
3426
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
3303
|
-
m('input', Object.assign(Object.assign({ class: shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
|
|
3304
|
-
placeholder, value: controlled ? value : undefined,
|
|
3427
|
+
m('input', Object.assign(Object.assign({ class: type === 'range' && attrs.vertical ? 'range-slider vertical' : shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
|
|
3428
|
+
placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
|
|
3305
3429
|
? {
|
|
3306
3430
|
height: attrs.height || '200px',
|
|
3307
3431
|
width: '6px',
|
|
@@ -4894,7 +5018,7 @@ const ModalPanel = () => {
|
|
|
4894
5018
|
closeModal(attrs);
|
|
4895
5019
|
}
|
|
4896
5020
|
}
|
|
4897
|
-
const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, } = attrs;
|
|
5021
|
+
const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, closeOnButtonClick = true, } = attrs;
|
|
4898
5022
|
const modalClasses = [
|
|
4899
5023
|
'modal',
|
|
4900
5024
|
className || '',
|
|
@@ -4991,7 +5115,7 @@ const ModalPanel = () => {
|
|
|
4991
5115
|
}, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
|
|
4992
5116
|
if (buttonProps.onclick)
|
|
4993
5117
|
buttonProps.onclick(e);
|
|
4994
|
-
closeModal(attrs);
|
|
5118
|
+
closeOnButtonClick && closeModal(attrs);
|
|
4995
5119
|
} })))),
|
|
4996
5120
|
]),
|
|
4997
5121
|
]);
|
|
@@ -5169,6 +5293,7 @@ const defaultOptions = {
|
|
|
5169
5293
|
autoClose: false,
|
|
5170
5294
|
twelveHour: true,
|
|
5171
5295
|
vibrate: true,
|
|
5296
|
+
roundBy5: false,
|
|
5172
5297
|
onOpen: () => { },
|
|
5173
5298
|
onOpenStart: () => { },
|
|
5174
5299
|
onOpenEnd: () => { },
|
|
@@ -5220,7 +5345,7 @@ const TimePicker = () => {
|
|
|
5220
5345
|
const clickPos = getPos(e);
|
|
5221
5346
|
state.dx = clickPos.x - state.x0;
|
|
5222
5347
|
state.dy = clickPos.y - state.y0;
|
|
5223
|
-
setHand(state.dx, state.dy,
|
|
5348
|
+
setHand(state.dx, state.dy, options.roundBy5);
|
|
5224
5349
|
document.addEventListener('mousemove', handleDocumentClickMove);
|
|
5225
5350
|
document.addEventListener('touchmove', handleDocumentClickMove);
|
|
5226
5351
|
document.addEventListener('mouseup', handleDocumentClickEnd);
|
|
@@ -5232,7 +5357,7 @@ const TimePicker = () => {
|
|
|
5232
5357
|
const x = clickPos.x - state.x0;
|
|
5233
5358
|
const y = clickPos.y - state.y0;
|
|
5234
5359
|
state.moved = true;
|
|
5235
|
-
setHand(x, y,
|
|
5360
|
+
setHand(x, y, options.roundBy5);
|
|
5236
5361
|
m.redraw();
|
|
5237
5362
|
};
|
|
5238
5363
|
const handleDocumentClickEnd = (e) => {
|
|
@@ -5668,6 +5793,66 @@ const TimePicker = () => {
|
|
|
5668
5793
|
},
|
|
5669
5794
|
};
|
|
5670
5795
|
};
|
|
5796
|
+
const handleKeyDown = (e) => {
|
|
5797
|
+
if (e.key === 'Escape' && state.isOpen) {
|
|
5798
|
+
close();
|
|
5799
|
+
clearPortal(state.portalContainerId);
|
|
5800
|
+
m.redraw();
|
|
5801
|
+
}
|
|
5802
|
+
};
|
|
5803
|
+
const renderPickerToPortal = (attrs) => {
|
|
5804
|
+
const { showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel' } = attrs;
|
|
5805
|
+
const pickerModal = m('.timepicker-modal-wrapper', {
|
|
5806
|
+
style: {
|
|
5807
|
+
position: 'fixed',
|
|
5808
|
+
top: '0',
|
|
5809
|
+
left: '0',
|
|
5810
|
+
width: '100%',
|
|
5811
|
+
height: '100%',
|
|
5812
|
+
pointerEvents: 'auto',
|
|
5813
|
+
display: 'flex',
|
|
5814
|
+
alignItems: 'center',
|
|
5815
|
+
justifyContent: 'center',
|
|
5816
|
+
},
|
|
5817
|
+
}, [
|
|
5818
|
+
// Modal overlay
|
|
5819
|
+
m('.modal-overlay', {
|
|
5820
|
+
style: {
|
|
5821
|
+
position: 'absolute',
|
|
5822
|
+
top: '0',
|
|
5823
|
+
left: '0',
|
|
5824
|
+
width: '100%',
|
|
5825
|
+
height: '100%',
|
|
5826
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
5827
|
+
zIndex: '1002',
|
|
5828
|
+
},
|
|
5829
|
+
onclick: () => {
|
|
5830
|
+
close();
|
|
5831
|
+
m.redraw();
|
|
5832
|
+
},
|
|
5833
|
+
}),
|
|
5834
|
+
// Modal content
|
|
5835
|
+
m('.modal.timepicker-modal.open', {
|
|
5836
|
+
style: {
|
|
5837
|
+
position: 'relative',
|
|
5838
|
+
zIndex: '1003',
|
|
5839
|
+
display: 'block',
|
|
5840
|
+
opacity: 1,
|
|
5841
|
+
top: 'auto',
|
|
5842
|
+
transform: 'scaleX(1) scaleY(1)',
|
|
5843
|
+
margin: '0 auto',
|
|
5844
|
+
},
|
|
5845
|
+
}, [
|
|
5846
|
+
m(TimepickerModal, {
|
|
5847
|
+
showClearBtn,
|
|
5848
|
+
clearLabel,
|
|
5849
|
+
closeLabel,
|
|
5850
|
+
doneLabel: 'OK',
|
|
5851
|
+
}),
|
|
5852
|
+
]),
|
|
5853
|
+
]);
|
|
5854
|
+
renderToPortal(state.portalContainerId, pickerModal, 1004);
|
|
5855
|
+
};
|
|
5671
5856
|
return {
|
|
5672
5857
|
oninit: (vnode) => {
|
|
5673
5858
|
const attrs = vnode.attrs;
|
|
@@ -5684,11 +5869,14 @@ const TimePicker = () => {
|
|
|
5684
5869
|
y0: 0,
|
|
5685
5870
|
dx: 0,
|
|
5686
5871
|
dy: 0,
|
|
5872
|
+
portalContainerId: `timepicker-portal-${uniqueId()}`,
|
|
5687
5873
|
};
|
|
5688
5874
|
// Handle value after options are set
|
|
5689
5875
|
if (attrs.defaultValue) {
|
|
5690
5876
|
updateTimeFromInput(attrs.defaultValue);
|
|
5691
5877
|
}
|
|
5878
|
+
// Add ESC key listener
|
|
5879
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
5692
5880
|
},
|
|
5693
5881
|
onremove: () => {
|
|
5694
5882
|
// Cleanup
|
|
@@ -5702,6 +5890,21 @@ const TimePicker = () => {
|
|
|
5702
5890
|
document.removeEventListener('touchmove', handleDocumentClickMove);
|
|
5703
5891
|
document.removeEventListener('mouseup', handleDocumentClickEnd);
|
|
5704
5892
|
document.removeEventListener('touchend', handleDocumentClickEnd);
|
|
5893
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
5894
|
+
// Clean up portal if picker was open
|
|
5895
|
+
if (state.isOpen) {
|
|
5896
|
+
clearPortal(state.portalContainerId);
|
|
5897
|
+
}
|
|
5898
|
+
},
|
|
5899
|
+
onupdate: (vnode) => {
|
|
5900
|
+
const { useModal = true } = vnode.attrs;
|
|
5901
|
+
// Only render to portal when using modal mode
|
|
5902
|
+
if (useModal && state.isOpen) {
|
|
5903
|
+
renderPickerToPortal(vnode.attrs);
|
|
5904
|
+
}
|
|
5905
|
+
else {
|
|
5906
|
+
clearPortal(state.portalContainerId);
|
|
5907
|
+
}
|
|
5705
5908
|
},
|
|
5706
5909
|
view: ({ attrs }) => {
|
|
5707
5910
|
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;
|
|
@@ -5798,29 +6001,7 @@ const TimePicker = () => {
|
|
|
5798
6001
|
}, label),
|
|
5799
6002
|
// Helper text
|
|
5800
6003
|
helperText && m('span.helper-text', helperText),
|
|
5801
|
-
// Modal
|
|
5802
|
-
useModal &&
|
|
5803
|
-
state.isOpen && [
|
|
5804
|
-
// Modal overlay
|
|
5805
|
-
m('.modal-overlay', {
|
|
5806
|
-
style: {
|
|
5807
|
-
zIndex: 1002,
|
|
5808
|
-
display: 'block',
|
|
5809
|
-
opacity: 0.5,
|
|
5810
|
-
},
|
|
5811
|
-
onclick: () => close(),
|
|
5812
|
-
}),
|
|
5813
|
-
// Modal content
|
|
5814
|
-
m('.modal.timepicker-modal.open', {
|
|
5815
|
-
style: {
|
|
5816
|
-
zIndex: 1003,
|
|
5817
|
-
display: 'block',
|
|
5818
|
-
opacity: 1,
|
|
5819
|
-
top: '10%',
|
|
5820
|
-
transform: 'scaleX(1) scaleY(1)',
|
|
5821
|
-
},
|
|
5822
|
-
}, m(TimepickerModal, { showClearBtn, clearLabel, closeLabel, doneLabel: 'OK' })),
|
|
5823
|
-
],
|
|
6004
|
+
// Modal is now rendered via portal in onupdate hook
|
|
5824
6005
|
]);
|
|
5825
6006
|
},
|
|
5826
6007
|
};
|
|
@@ -9543,4 +9724,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
9543
9724
|
// ============================================================================
|
|
9544
9725
|
// All types are already exported via individual export declarations above
|
|
9545
9726
|
|
|
9546
|
-
export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Timeline, Toast, ToastComponent, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, createBreadcrumb, getDropdownStyles, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, toast, uniqueId, uuid4 };
|
|
9727
|
+
export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Timeline, Toast, ToastComponent, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, clearPortal, createBreadcrumb, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, releasePortalContainer, renderToPortal, toast, uniqueId, uuid4 };
|