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.umd.js CHANGED
@@ -163,6 +163,76 @@
163
163
  * @example: console.log(range(5, 10)); // [5, 6, 7, 8, 9, 10]
164
164
  */
165
165
  const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
166
+ // Global registry for portal containers
167
+ const portalContainers = new Map();
168
+ /**
169
+ * Creates or retrieves a portal container appended to document.body.
170
+ * Uses reference counting to manage container lifecycle.
171
+ *
172
+ * @param id - Unique identifier for the portal container
173
+ * @param zIndex - Z-index for the portal container (default: 1004, above modals at 1003)
174
+ * @returns The portal container element
175
+ */
176
+ const getPortalContainer = (id, zIndex = 1004) => {
177
+ let container = portalContainers.get(id);
178
+ if (!container) {
179
+ const element = document.createElement('div');
180
+ element.id = id;
181
+ element.style.position = 'fixed';
182
+ element.style.top = '0';
183
+ element.style.left = '0';
184
+ element.style.width = '100%';
185
+ element.style.height = '100%';
186
+ element.style.pointerEvents = 'none'; // Allow clicks through to underlying elements
187
+ element.style.zIndex = zIndex.toString();
188
+ document.body.appendChild(element);
189
+ container = { element, refCount: 0 };
190
+ portalContainers.set(id, container);
191
+ }
192
+ container.refCount++;
193
+ return container.element;
194
+ };
195
+ /**
196
+ * Decrements reference count and removes portal container if no longer needed.
197
+ *
198
+ * @param id - Portal container identifier
199
+ */
200
+ const releasePortalContainer = (id) => {
201
+ const container = portalContainers.get(id);
202
+ if (container) {
203
+ container.refCount--;
204
+ if (container.refCount <= 0) {
205
+ container.element.remove();
206
+ portalContainers.delete(id);
207
+ }
208
+ }
209
+ };
210
+ /**
211
+ * Renders a Mithril vnode into a portal container using m.render().
212
+ * This allows components to render outside their parent DOM hierarchy,
213
+ * useful for modals and pickers that need to escape stacking contexts.
214
+ *
215
+ * @param containerId - Portal container identifier
216
+ * @param vnode - Mithril vnode to render
217
+ * @param zIndex - Z-index for portal container (default: 1004)
218
+ */
219
+ const renderToPortal = (containerId, vnode, zIndex = 1004) => {
220
+ const container = getPortalContainer(containerId, zIndex);
221
+ m.render(container, vnode);
222
+ };
223
+ /**
224
+ * Clears portal content and releases container reference.
225
+ * If this is the last reference, the container will be removed from the DOM.
226
+ *
227
+ * @param containerId - Portal container identifier
228
+ */
229
+ const clearPortal = (containerId) => {
230
+ const container = portalContainers.get(containerId);
231
+ if (container) {
232
+ m.render(container.element, null);
233
+ releasePortalContainer(containerId);
234
+ }
235
+ };
166
236
 
167
237
  // import './styles/input.css';
168
238
  const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
@@ -2191,6 +2261,129 @@
2191
2261
  }
2192
2262
  m.redraw();
2193
2263
  };
2264
+ const handleKeyDown = (e) => {
2265
+ if (e.key === 'Escape' && state.isOpen) {
2266
+ state.isOpen = false;
2267
+ const options = mergeOptions({});
2268
+ if (options.onClose)
2269
+ options.onClose();
2270
+ clearPortal(state.portalContainerId);
2271
+ m.redraw();
2272
+ }
2273
+ };
2274
+ const renderPickerToPortal = (attrs) => {
2275
+ const options = mergeOptions(attrs);
2276
+ const pickerModal = m('.datepicker-modal-wrapper', {
2277
+ style: {
2278
+ position: 'fixed',
2279
+ top: '0',
2280
+ left: '0',
2281
+ width: '100%',
2282
+ height: '100%',
2283
+ pointerEvents: 'auto',
2284
+ display: 'flex',
2285
+ alignItems: 'center',
2286
+ justifyContent: 'center',
2287
+ },
2288
+ }, [
2289
+ // Modal overlay
2290
+ m('.modal-overlay', {
2291
+ style: {
2292
+ position: 'absolute',
2293
+ top: '0',
2294
+ left: '0',
2295
+ width: '100%',
2296
+ height: '100%',
2297
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
2298
+ zIndex: '1002',
2299
+ },
2300
+ onclick: () => {
2301
+ state.isOpen = false;
2302
+ if (options.onClose)
2303
+ options.onClose();
2304
+ m.redraw();
2305
+ },
2306
+ }),
2307
+ // Modal content
2308
+ m('.modal.datepicker-modal.open', {
2309
+ id: `modal-${state.id}`,
2310
+ tabindex: 0,
2311
+ style: {
2312
+ position: 'relative',
2313
+ zIndex: '1003',
2314
+ display: 'block',
2315
+ opacity: 1,
2316
+ top: 'auto',
2317
+ transform: 'scaleX(1) scaleY(1)',
2318
+ margin: '0 auto',
2319
+ },
2320
+ }, [
2321
+ m('.modal-content.datepicker-container', {
2322
+ onclick: (e) => {
2323
+ const target = e.target;
2324
+ if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
2325
+ state.monthDropdownOpen = false;
2326
+ state.yearDropdownOpen = false;
2327
+ }
2328
+ },
2329
+ }, [
2330
+ m(DateDisplay, { options }),
2331
+ m('.datepicker-calendar-container', [
2332
+ m('.datepicker-calendar', [
2333
+ m(DateControls, {
2334
+ options,
2335
+ randId: `datepicker-title-${Math.random().toString(36).slice(2)}`,
2336
+ }),
2337
+ m(Calendar, {
2338
+ year: state.calendars[0].year,
2339
+ month: state.calendars[0].month,
2340
+ options,
2341
+ }),
2342
+ ]),
2343
+ m('.datepicker-footer', [
2344
+ options.showClearBtn &&
2345
+ m('button.btn-flat.datepicker-clear.waves-effect', {
2346
+ type: 'button',
2347
+ onclick: () => {
2348
+ setDate(null, false, options);
2349
+ state.isOpen = false;
2350
+ },
2351
+ }, options.i18n.clear),
2352
+ m('button.btn-flat.datepicker-cancel.waves-effect', {
2353
+ type: 'button',
2354
+ onclick: () => {
2355
+ state.isOpen = false;
2356
+ if (options.onClose)
2357
+ options.onClose();
2358
+ },
2359
+ }, options.i18n.cancel),
2360
+ m('button.btn-flat.datepicker-done.waves-effect', {
2361
+ type: 'button',
2362
+ onclick: () => {
2363
+ state.isOpen = false;
2364
+ if (options.dateRange) {
2365
+ if (state.startDate && state.endDate && attrs.onchange) {
2366
+ const startStr = formatDate(state.startDate, 'yyyy-mm-dd', options);
2367
+ const endStr = formatDate(state.endDate, 'yyyy-mm-dd', options);
2368
+ attrs.onchange(`${startStr} - ${endStr}`);
2369
+ }
2370
+ }
2371
+ else {
2372
+ if (state.date && attrs.onchange) {
2373
+ attrs.onchange(toString(state.date, 'yyyy-mm-dd'));
2374
+ }
2375
+ }
2376
+ if (options.onClose)
2377
+ options.onClose();
2378
+ },
2379
+ }, options.i18n.done),
2380
+ ]),
2381
+ ]),
2382
+ ]),
2383
+ ]),
2384
+ ]);
2385
+ renderToPortal(state.portalContainerId, pickerModal, 1004);
2386
+ };
2194
2387
  return {
2195
2388
  oninit: (vnode) => {
2196
2389
  const attrs = vnode.attrs;
@@ -2206,6 +2399,7 @@
2206
2399
  calendars: [{ month: 0, year: 0 }],
2207
2400
  monthDropdownOpen: false,
2208
2401
  yearDropdownOpen: false,
2402
+ portalContainerId: `datepicker-portal-${uniqueId()}`,
2209
2403
  formats: {
2210
2404
  d: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDate()) || 0; },
2211
2405
  dd: () => {
@@ -2259,10 +2453,26 @@
2259
2453
  }
2260
2454
  // Add document click listener to close dropdowns
2261
2455
  document.addEventListener('click', handleDocumentClick);
2456
+ // Add ESC key listener
2457
+ document.addEventListener('keydown', handleKeyDown);
2262
2458
  },
2263
2459
  onremove: () => {
2264
- // Clean up event listener
2460
+ // Clean up event listeners
2265
2461
  document.removeEventListener('click', handleDocumentClick);
2462
+ document.removeEventListener('keydown', handleKeyDown);
2463
+ // Clean up portal if picker was open
2464
+ if (state.isOpen) {
2465
+ clearPortal(state.portalContainerId);
2466
+ }
2467
+ },
2468
+ onupdate: (vnode) => {
2469
+ // Render to portal when picker is open, clear when closed
2470
+ if (state.isOpen) {
2471
+ renderPickerToPortal(vnode.attrs);
2472
+ }
2473
+ else {
2474
+ clearPortal(state.portalContainerId);
2475
+ }
2266
2476
  },
2267
2477
  view: (vnode) => {
2268
2478
  const attrs = vnode.attrs;
@@ -2378,93 +2588,7 @@
2378
2588
  }, label || dateLabel),
2379
2589
  // Helper text
2380
2590
  helperText && m('span.helper-text', helperText),
2381
- // Modal datepicker
2382
- state.isOpen && [
2383
- m('.modal.datepicker-modal.open', {
2384
- id: `modal-${state.id}`,
2385
- tabindex: 0,
2386
- style: {
2387
- zIndex: 1003,
2388
- display: 'block',
2389
- opacity: 1,
2390
- top: '10%',
2391
- transform: 'scaleX(1) scaleY(1)',
2392
- },
2393
- }, [
2394
- m('.modal-content.datepicker-container', {
2395
- onclick: (e) => {
2396
- // Close dropdowns when clicking anywhere in the modal content
2397
- const target = e.target;
2398
- if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
2399
- state.monthDropdownOpen = false;
2400
- state.yearDropdownOpen = false;
2401
- }
2402
- },
2403
- }, [
2404
- m(DateDisplay, { options }),
2405
- m('.datepicker-calendar-container', [
2406
- m('.datepicker-calendar', [
2407
- m(DateControls, { options, randId: `datepicker-title-${Math.random().toString(36).slice(2)}` }),
2408
- m(Calendar, { year: state.calendars[0].year, month: state.calendars[0].month, options }),
2409
- ]),
2410
- m('.datepicker-footer', [
2411
- options.showClearBtn &&
2412
- m('button.btn-flat.datepicker-clear.waves-effect', {
2413
- type: 'button',
2414
- style: '',
2415
- onclick: () => {
2416
- setDate(null, false, options);
2417
- state.isOpen = false;
2418
- },
2419
- }, options.i18n.clear),
2420
- m('button.btn-flat.datepicker-cancel.waves-effect', {
2421
- type: 'button',
2422
- onclick: () => {
2423
- state.isOpen = false;
2424
- if (options.onClose)
2425
- options.onClose();
2426
- },
2427
- }, options.i18n.cancel),
2428
- m('button.btn-flat.datepicker-done.waves-effect', {
2429
- type: 'button',
2430
- onclick: () => {
2431
- state.isOpen = false;
2432
- if (options.dateRange) {
2433
- // Range mode
2434
- if (state.startDate && state.endDate && onchange) {
2435
- const startStr = toString(state.startDate, 'yyyy-mm-dd');
2436
- const endStr = toString(state.endDate, 'yyyy-mm-dd');
2437
- onchange(`${startStr} - ${endStr}`);
2438
- }
2439
- }
2440
- else {
2441
- // Single date mode
2442
- if (state.date && onchange) {
2443
- onchange(toString(state.date, 'yyyy-mm-dd')); // Always return ISO format
2444
- }
2445
- }
2446
- if (options.onClose)
2447
- options.onClose();
2448
- },
2449
- }, options.i18n.done),
2450
- ]),
2451
- ]),
2452
- ]),
2453
- ]),
2454
- // Modal overlay
2455
- m('.modal-overlay', {
2456
- style: {
2457
- zIndex: 1002,
2458
- display: 'block',
2459
- opacity: 0.5,
2460
- },
2461
- onclick: () => {
2462
- state.isOpen = false;
2463
- if (options.onClose)
2464
- options.onClose();
2465
- },
2466
- }),
2467
- ],
2591
+ // Modal is now rendered via portal in onupdate hook
2468
2592
  ]);
2469
2593
  },
2470
2594
  };
@@ -3304,8 +3428,8 @@
3304
3428
  const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
3305
3429
  return m('.input-field', { className: cn, style }, [
3306
3430
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
3307
- m('input', Object.assign(Object.assign({ class: shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
3308
- placeholder, value: controlled ? value : undefined, class: type === 'range' && attrs.vertical ? 'range-slider vertical' : undefined, style: type === 'range' && attrs.vertical
3431
+ m('input', Object.assign(Object.assign({ class: type === 'range' && attrs.vertical ? 'range-slider vertical' : shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
3432
+ placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
3309
3433
  ? {
3310
3434
  height: attrs.height || '200px',
3311
3435
  width: '6px',
@@ -4898,7 +5022,7 @@
4898
5022
  closeModal(attrs);
4899
5023
  }
4900
5024
  }
4901
- const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, } = attrs;
5025
+ const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, closeOnButtonClick = true, } = attrs;
4902
5026
  const modalClasses = [
4903
5027
  'modal',
4904
5028
  className || '',
@@ -4995,7 +5119,7 @@
4995
5119
  }, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
4996
5120
  if (buttonProps.onclick)
4997
5121
  buttonProps.onclick(e);
4998
- closeModal(attrs);
5122
+ closeOnButtonClick && closeModal(attrs);
4999
5123
  } })))),
5000
5124
  ]),
5001
5125
  ]);
@@ -5173,6 +5297,7 @@
5173
5297
  autoClose: false,
5174
5298
  twelveHour: true,
5175
5299
  vibrate: true,
5300
+ roundBy5: false,
5176
5301
  onOpen: () => { },
5177
5302
  onOpenStart: () => { },
5178
5303
  onOpenEnd: () => { },
@@ -5224,7 +5349,7 @@
5224
5349
  const clickPos = getPos(e);
5225
5350
  state.dx = clickPos.x - state.x0;
5226
5351
  state.dy = clickPos.y - state.y0;
5227
- setHand(state.dx, state.dy, false);
5352
+ setHand(state.dx, state.dy, options.roundBy5);
5228
5353
  document.addEventListener('mousemove', handleDocumentClickMove);
5229
5354
  document.addEventListener('touchmove', handleDocumentClickMove);
5230
5355
  document.addEventListener('mouseup', handleDocumentClickEnd);
@@ -5236,7 +5361,7 @@
5236
5361
  const x = clickPos.x - state.x0;
5237
5362
  const y = clickPos.y - state.y0;
5238
5363
  state.moved = true;
5239
- setHand(x, y, false);
5364
+ setHand(x, y, options.roundBy5);
5240
5365
  m.redraw();
5241
5366
  };
5242
5367
  const handleDocumentClickEnd = (e) => {
@@ -5672,6 +5797,66 @@
5672
5797
  },
5673
5798
  };
5674
5799
  };
5800
+ const handleKeyDown = (e) => {
5801
+ if (e.key === 'Escape' && state.isOpen) {
5802
+ close();
5803
+ clearPortal(state.portalContainerId);
5804
+ m.redraw();
5805
+ }
5806
+ };
5807
+ const renderPickerToPortal = (attrs) => {
5808
+ const { showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel' } = attrs;
5809
+ const pickerModal = m('.timepicker-modal-wrapper', {
5810
+ style: {
5811
+ position: 'fixed',
5812
+ top: '0',
5813
+ left: '0',
5814
+ width: '100%',
5815
+ height: '100%',
5816
+ pointerEvents: 'auto',
5817
+ display: 'flex',
5818
+ alignItems: 'center',
5819
+ justifyContent: 'center',
5820
+ },
5821
+ }, [
5822
+ // Modal overlay
5823
+ m('.modal-overlay', {
5824
+ style: {
5825
+ position: 'absolute',
5826
+ top: '0',
5827
+ left: '0',
5828
+ width: '100%',
5829
+ height: '100%',
5830
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
5831
+ zIndex: '1002',
5832
+ },
5833
+ onclick: () => {
5834
+ close();
5835
+ m.redraw();
5836
+ },
5837
+ }),
5838
+ // Modal content
5839
+ m('.modal.timepicker-modal.open', {
5840
+ style: {
5841
+ position: 'relative',
5842
+ zIndex: '1003',
5843
+ display: 'block',
5844
+ opacity: 1,
5845
+ top: 'auto',
5846
+ transform: 'scaleX(1) scaleY(1)',
5847
+ margin: '0 auto',
5848
+ },
5849
+ }, [
5850
+ m(TimepickerModal, {
5851
+ showClearBtn,
5852
+ clearLabel,
5853
+ closeLabel,
5854
+ doneLabel: 'OK',
5855
+ }),
5856
+ ]),
5857
+ ]);
5858
+ renderToPortal(state.portalContainerId, pickerModal, 1004);
5859
+ };
5675
5860
  return {
5676
5861
  oninit: (vnode) => {
5677
5862
  const attrs = vnode.attrs;
@@ -5688,11 +5873,14 @@
5688
5873
  y0: 0,
5689
5874
  dx: 0,
5690
5875
  dy: 0,
5876
+ portalContainerId: `timepicker-portal-${uniqueId()}`,
5691
5877
  };
5692
5878
  // Handle value after options are set
5693
5879
  if (attrs.defaultValue) {
5694
5880
  updateTimeFromInput(attrs.defaultValue);
5695
5881
  }
5882
+ // Add ESC key listener
5883
+ document.addEventListener('keydown', handleKeyDown);
5696
5884
  },
5697
5885
  onremove: () => {
5698
5886
  // Cleanup
@@ -5706,6 +5894,21 @@
5706
5894
  document.removeEventListener('touchmove', handleDocumentClickMove);
5707
5895
  document.removeEventListener('mouseup', handleDocumentClickEnd);
5708
5896
  document.removeEventListener('touchend', handleDocumentClickEnd);
5897
+ document.removeEventListener('keydown', handleKeyDown);
5898
+ // Clean up portal if picker was open
5899
+ if (state.isOpen) {
5900
+ clearPortal(state.portalContainerId);
5901
+ }
5902
+ },
5903
+ onupdate: (vnode) => {
5904
+ const { useModal = true } = vnode.attrs;
5905
+ // Only render to portal when using modal mode
5906
+ if (useModal && state.isOpen) {
5907
+ renderPickerToPortal(vnode.attrs);
5908
+ }
5909
+ else {
5910
+ clearPortal(state.portalContainerId);
5911
+ }
5709
5912
  },
5710
5913
  view: ({ attrs }) => {
5711
5914
  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;
@@ -5802,29 +6005,7 @@
5802
6005
  }, label),
5803
6006
  // Helper text
5804
6007
  helperText && m('span.helper-text', helperText),
5805
- // Modal timepicker
5806
- useModal &&
5807
- state.isOpen && [
5808
- // Modal overlay
5809
- m('.modal-overlay', {
5810
- style: {
5811
- zIndex: 1002,
5812
- display: 'block',
5813
- opacity: 0.5,
5814
- },
5815
- onclick: () => close(),
5816
- }),
5817
- // Modal content
5818
- m('.modal.timepicker-modal.open', {
5819
- style: {
5820
- zIndex: 1003,
5821
- display: 'block',
5822
- opacity: 1,
5823
- top: '10%',
5824
- transform: 'scaleX(1) scaleY(1)',
5825
- },
5826
- }, m(TimepickerModal, { showClearBtn, clearLabel, closeLabel, doneLabel: 'OK' })),
5827
- ],
6008
+ // Modal is now rendered via portal in onupdate hook
5828
6009
  ]);
5829
6010
  },
5830
6011
  };
@@ -9623,8 +9804,10 @@
9623
9804
  exports.TreeView = TreeView;
9624
9805
  exports.UrlInput = UrlInput;
9625
9806
  exports.Wizard = Wizard;
9807
+ exports.clearPortal = clearPortal;
9626
9808
  exports.createBreadcrumb = createBreadcrumb;
9627
9809
  exports.getDropdownStyles = getDropdownStyles;
9810
+ exports.getPortalContainer = getPortalContainer;
9628
9811
  exports.initPushpins = initPushpins;
9629
9812
  exports.initTooltips = initTooltips;
9630
9813
  exports.isNumeric = isNumeric;
@@ -9632,6 +9815,8 @@
9632
9815
  exports.isValidationSuccess = isValidationSuccess;
9633
9816
  exports.padLeft = padLeft;
9634
9817
  exports.range = range;
9818
+ exports.releasePortalContainer = releasePortalContainer;
9819
+ exports.renderToPortal = renderToPortal;
9635
9820
  exports.toast = toast;
9636
9821
  exports.uniqueId = uniqueId;
9637
9822
  exports.uuid4 = uuid4;
package/dist/modal.d.ts CHANGED
@@ -27,10 +27,12 @@ export interface ModalAttrs extends Attributes {
27
27
  onToggle?: (open: boolean) => void;
28
28
  /** Called when modal is closed */
29
29
  onClose?: () => void;
30
- /** Show close button in top right */
30
+ /** Show close button in top right (default true) */
31
31
  showCloseButton?: boolean;
32
- /** Close modal when clicking backdrop */
32
+ /** Close modal when clicking backdrop (default true) */
33
33
  closeOnBackdropClick?: boolean;
34
+ /** Close modal when clicking a button (default true) */
35
+ closeOnButtonClick?: boolean;
34
36
  /** Close modal when pressing escape key */
35
37
  closeOnEsc?: boolean;
36
38
  }
@@ -19,6 +19,7 @@ export interface TimepickerOptions {
19
19
  autoClose?: boolean;
20
20
  twelveHour?: boolean;
21
21
  vibrate?: boolean;
22
+ roundBy5?: boolean;
22
23
  onOpen?: () => void;
23
24
  onOpenStart?: () => void;
24
25
  onOpenEnd?: () => void;
package/dist/utils.d.ts CHANGED
@@ -41,3 +41,35 @@ export declare const getDropdownStyles: (inputRef?: HTMLElement | null, overlap?
41
41
  * @example: console.log(range(5, 10)); // [5, 6, 7, 8, 9, 10]
42
42
  */
43
43
  export declare const range: (a: number, b: number) => number[];
44
+ /**
45
+ * Creates or retrieves a portal container appended to document.body.
46
+ * Uses reference counting to manage container lifecycle.
47
+ *
48
+ * @param id - Unique identifier for the portal container
49
+ * @param zIndex - Z-index for the portal container (default: 1004, above modals at 1003)
50
+ * @returns The portal container element
51
+ */
52
+ export declare const getPortalContainer: (id: string, zIndex?: number) => HTMLElement;
53
+ /**
54
+ * Decrements reference count and removes portal container if no longer needed.
55
+ *
56
+ * @param id - Portal container identifier
57
+ */
58
+ export declare const releasePortalContainer: (id: string) => void;
59
+ /**
60
+ * Renders a Mithril vnode into a portal container using m.render().
61
+ * This allows components to render outside their parent DOM hierarchy,
62
+ * useful for modals and pickers that need to escape stacking contexts.
63
+ *
64
+ * @param containerId - Portal container identifier
65
+ * @param vnode - Mithril vnode to render
66
+ * @param zIndex - Z-index for portal container (default: 1004)
67
+ */
68
+ export declare const renderToPortal: (containerId: string, vnode: any, zIndex?: number) => void;
69
+ /**
70
+ * Clears portal content and releases container reference.
71
+ * If this is the last reference, the container will be removed from the DOM.
72
+ *
73
+ * @param containerId - Portal container identifier
74
+ */
75
+ export declare const clearPortal: (containerId: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.5.5",
3
+ "version": "3.5.7",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -38,7 +38,7 @@
38
38
  "build": "rollup -c rollup.config.mjs && npm run build:css-min",
39
39
  "build:css-min": "sass --no-source-map --style=compressed --no-charset ./src/index.scss ./dist/index.min.css && npm run build:css-modules",
40
40
  "build:css-modules": "sass --no-source-map ./src/core.scss ./dist/core.css && sass --no-source-map ./src/components.scss ./dist/components.css && sass --no-source-map ./src/forms.scss ./dist/forms.css && sass --no-source-map ./src/pickers.scss ./dist/pickers.css && sass --no-source-map ./src/advanced.scss ./dist/advanced.css && sass --no-source-map ./src/utilities.scss ./dist/utilities.css",
41
- "dev": "concurrently \"rollup -c rollup.config.mjs -w\" \"npm run css:watch\"",
41
+ "dev": "concurrently \"rollup -c rollup.config.mjs -w\" \"pnpm run css:watch\"",
42
42
  "start": "pnpm run dev",
43
43
  "clean": "rimraf dist node_modules/.cache",
44
44
  "test": "jest",
@@ -55,7 +55,7 @@
55
55
  "dry-run": "npm publish --dry-run",
56
56
  "sass:watch": "sass --watch ./sass/materialize.scss ./dist/index.css",
57
57
  "css:watch": "sass --watch --no-source-map --style=compressed --no-charset ./src/index.scss ./dist/index.min.css",
58
- "dev:full": "npm run dev & npm run css:watch",
58
+ "dev:full": "pnpm run dev & pnpm run css:watch",
59
59
  "patch-release": "npm run clean && npm run build && npm version patch --force -m \"Patch release\" && npm publish && git push --follow-tags",
60
60
  "minor-release": "npm run clean && npm run build && npm version minor --force -m \"Minor release\" && npm publish && git push --follow-tags",
61
61
  "major-release": "npm run clean && npm run build && npm version major --force -m \"Major release\" && npm publish && git push --follow-tags"
@@ -72,30 +72,29 @@
72
72
  "author": "Erik Vullings <erik.vullings@gmail.com> (http://www.tno.nl)",
73
73
  "license": "MIT",
74
74
  "dependencies": {
75
- "mithril": "^2.3.7"
75
+ "mithril": "^2.3.8"
76
76
  },
77
77
  "devDependencies": {
78
- "@playwright/test": "^1.56.1",
78
+ "@playwright/test": "^1.57.0",
79
79
  "@rollup/plugin-typescript": "^12.3.0",
80
80
  "@testing-library/dom": "^10.4.1",
81
81
  "@testing-library/jest-dom": "^6.9.1",
82
82
  "@testing-library/user-event": "^14.6.1",
83
83
  "@types/jest": "^30.0.0",
84
84
  "@types/mithril": "^2.2.7",
85
- "autoprefixer": "^10.4.21",
85
+ "autoprefixer": "^10.4.23",
86
86
  "concurrently": "^9.2.1",
87
- "express": "^5.1.0",
88
87
  "identity-obj-proxy": "^3.0.0",
89
88
  "jest": "^30.2.0",
90
89
  "jest-environment-jsdom": "^30.2.0",
91
- "js-yaml": "^4.1.0",
92
- "rimraf": "^6.1.0",
93
- "rollup": "^4.52.5",
90
+ "js-yaml": "^4.1.1",
91
+ "rimraf": "^6.1.2",
92
+ "rollup": "^4.54.0",
94
93
  "rollup-plugin-postcss": "^4.0.2",
95
- "sass": "^1.93.2",
96
- "ts-jest": "^29.4.5",
94
+ "sass": "^1.97.1",
95
+ "ts-jest": "^29.4.6",
97
96
  "tslib": "^2.8.1",
98
- "typedoc": "^0.28.14",
97
+ "typedoc": "^0.28.15",
99
98
  "typescript": "^5.9.3"
100
99
  }
101
100
  }