layerpro 4.0.0 → 4.0.1

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/README.md CHANGED
@@ -18,7 +18,11 @@
18
18
  ![OXlint](https://img.shields.io/badge/Oxlint-gray?logo=oxc)
19
19
  ![Playwright](https://img.shields.io/badge/Playwright-gray?logo=playwright)
20
20
 
21
- LayerPro replaces the browser's native `alert()`, `confirm()`, and `prompt()` with beautiful, fully customizable modals that work everywhere - no more blocking the main thread, no more default browser styling.
21
+ LayerPro is a browser window system: it generates **OS-style windows** - draggable, resizable, maximizable, dockable - that can host any content, including **React components**.
22
+
23
+ It also replaces the browser's native `alert()`, `confirm()`, and `prompt()` with beautiful, fully customizable modals that work everywhere - no more blocking the main thread, no more default browser styling.
24
+
25
+ LayerPro is **not only** a replacement for `alert`/`prompt`/`confirm`: the same window engine is used to build rich, interactive windows on top of your app.
22
26
 
23
27
  ## Table of Contents
24
28
 
@@ -28,15 +32,19 @@ LayerPro replaces the browser's native `alert()`, `confirm()`, and `prompt()` wi
28
32
  - [Usage](#usage)
29
33
  - [Basic Modals](#basic-modals)
30
34
  - [Custom Popups](#custom-popups)
35
+ - [OS-style Windows](#os-style-windows)
31
36
  - [React Components](#react-components)
32
37
  - [The `body` Parameter](#the-body-parameter)
33
38
  - [Options](#options)
34
39
  - [Global Overrides](#global-overrides)
35
40
  - [Methods](#methods)
36
41
  - [Styling & z-index](#styling--z-index)
42
+ - [Overriding the CSS](#overriding-the-css)
43
+ - [CSS custom properties](#1-css-custom-properties-recommended)
44
+ - [Target the generated classes](#2-target-the-generated-classes)
45
+ - [Cascade layers](#3-cascade-layers-full-control)
46
+ - [Dock](#dock)
37
47
  - [Security](#security)
38
- - [Browser Support](#browser-support)
39
- - [Contributing](#contributing)
40
48
  - [License](#license)
41
49
 
42
50
  ---
@@ -44,7 +52,9 @@ LayerPro replaces the browser's native `alert()`, `confirm()`, and `prompt()` wi
44
52
  ## Features
45
53
 
46
54
  - **Zero runtime dependencies** - no jQuery, pure vanilla DOM helpers; `react`/`react-dom` are peer dependencies, only loaded when React bodies are used, powered by [`reattach`](https://npmjs.com/package/reattach)
47
- - **Multiple modal types** - alert, confirm, prompt, message
55
+ - **OS-style window engine** - not just modals: every popup is a real window with a title bar, drag handle, resize handles, maximize/restore, minimize-to-dock, focus stacking, and a z-order you control
56
+ - **Hosts any content** - plain text, HTML strings, or **full React components** (with Context bridging and DOM auto-healing), rendered inside the window body
57
+ - **Multiple modal types** - alert, confirm, prompt, message - built on top of the same window engine, so they inherit the same styling, dock, and accessibility features
48
58
  - **Fully customizable** - appearance, behavior, and content
49
59
  - **Dockable windows** (taskbar-style) - click a dock icon to restore, focus, or minimize, just like an OS taskbar
50
60
  - **Draggable & resizable** - instant, with no transition lag while resizing
@@ -72,7 +82,7 @@ Import LayerPro once, in your app's entry point:
72
82
  import "layerpro";
73
83
  ```
74
84
 
75
- That's it - `alert()`, `confirm()`, `prompt()`, `message()`, and `popup()` are now available globally.
85
+ That's it - `layerpro.popup()`, `layerpro.alert()`, `layerpro.confirm()`, `layerpro.prompt()`, `layerpro.message()` and `layerpro.version()` are now available. Import `layerpro/global` instead if you also want the browser's own `alert`/`confirm`/`prompt` overridden (see [Global Overrides](#global-overrides-opt-in-in-40)).
76
86
 
77
87
  The `popup` alias is a shorthand for `layerpro.popup`:
78
88
 
@@ -89,19 +99,15 @@ popup.open({ id: 'myPopup', body: 'Hello World' });
89
99
  ### Basic Modals
90
100
 
91
101
  ```javascript
92
- alert("Hello World!");
93
-
94
- confirm("Are you sure?", () => {
95
- console.debug("User clicked YES");
96
- });
102
+ layerpro.alert("Hello World!"); // Promise<void>
103
+ layerpro.message("Operation complete!"); // Promise<void>
97
104
 
98
- prompt("What's your name?", (name) => {
99
- console.debug("User entered:", name);
100
- });
101
-
102
- message("Operation complete!");
105
+ const confirmed = await layerpro.confirm("Are you sure?"); // Promise<boolean>
106
+ const name = await layerpro.prompt("What's your name?"); // Promise<string>
103
107
  ```
104
108
 
109
+ > **Note:** with the default `import "layerpro"` entry the browser's own `alert`/`confirm`/`prompt` are **not** replaced. Use `import "layerpro/global"` to opt in to the global overrides, or call the `layerpro.*` methods directly as shown above.
110
+
105
111
  ### Custom Popups
106
112
 
107
113
  ```javascript
@@ -118,6 +124,48 @@ popup.open({
118
124
  });
119
125
  ```
120
126
 
127
+ ### OS-style Windows
128
+
129
+ A popup is not limited to a modal dialog. Turn on `movable`, `resizable`, `maximize`, `iconize` and `dockable` and it becomes a real OS-style window: draggable by its title bar, resizable from the edges, maximizable, minimizable into the dock, and stackable with the other windows on the page.
130
+
131
+ ```javascript
132
+ popup.open({
133
+ id: 'dashboard',
134
+ name: 'Dashboard',
135
+ icon: '&#128202;',
136
+ body: '<h2>Live metrics</h2><p>Anything you want inside.</p>',
137
+ width: 640,
138
+ height: 420,
139
+ movable: true,
140
+ resizable: true,
141
+ maximize: true,
142
+ iconize: true,
143
+ dockable: true,
144
+ close: true
145
+ });
146
+ ```
147
+
148
+ Combined with a React component, this becomes a real application window - a dashboard, a settings panel, a file browser - rendered inside the browser page with native-looking chrome:
149
+
150
+ ```jsx
151
+ popup.open({
152
+ id: 'settingsWindow',
153
+ name: 'Settings',
154
+ icon: '&#9881;',
155
+ body: <SettingsPanel />,
156
+ width: 720,
157
+ height: 480,
158
+ movable: true,
159
+ resizable: true,
160
+ maximize: true,
161
+ iconize: true,
162
+ dockable: true,
163
+ close: true
164
+ });
165
+ ```
166
+
167
+ > **Tip:** `modal: true` is what turns a window into a blocking dialog. Leave it off for windows the user should be able to move around while working on the page.
168
+
121
169
  ### React Components
122
170
 
123
171
  LayerPro's React adapter is powered by [`reattach`](https://npmjs.com/package/reattach), a zero-dependency library for mounting React components anywhere in the DOM. It handles root creation, automatic root reuse on duplicate-id reopen, **Context Bridging** across detached roots, and **auto-healing DOM** recovery - all without extra setup.
@@ -273,6 +321,7 @@ const val = await prompt("Name?"); // Promise<string>
273
321
  | `popup.movable(props)` | Enable dragging. |
274
322
  | `popup.resizable(props)` | Enable resizing. |
275
323
  | `layerpro.installGlobals()` | Override `window.alert/confirm/prompt/message` with LayerPro's Promise-based versions. |
324
+ | `layerpro.version()` | Returns the current LayerPro version string (read from `app.json`). |
276
325
  | `layerpro.config(opts)` | Runtime config. Currently supports `{ nonce: string }` for CSP; sets `window.__layerproNonce` consumed by the injected `<style>`. Call before importing LayerPro for the nonce to be present at injection. |
277
326
 
278
327
  ## Styling & z-index
@@ -298,6 +347,187 @@ The title bar, window surface, internal area, and body all read from CSS custom
298
347
  | `--modal-background` | modal overlay |
299
348
  | `--popup-z-index` | base z-index for the stack |
300
349
 
350
+ Additional variables available for buttons, inputs, resize handles, and the dock:
351
+
352
+ | Variable | Applies to |
353
+ |----------|-----------|
354
+ | `--button-background-color` / `--button-color` | confirm/cancel buttons |
355
+ | `--input-background-color` / `--input-color` | prompt input field |
356
+ | `--inputButton-fontSize` | button/input font size |
357
+ | `--resize-background-color` / `--resize-shadow` | resize handles |
358
+ | `--dock-background-color` / `--dock-height` | taskbar strip |
359
+ | `--dock-sideLeft` / `--dock-sideCenter` / `--dock-sideRight` | dock alignment |
360
+ | `--dock-icon-width` | dock icon width |
361
+ | `--transition-time` | fade/hover transitions |
362
+ | `--border-radius` / `--corner-size` / `--corner-border-radius` | resize handle shape |
363
+ | `--hover-opacity` / `--hover-filter` | hover feedback |
364
+
365
+ ## Overriding the CSS
366
+
367
+ LayerPro ships its styles **injected at runtime** into a `<style>` tag (no separate CSS import needed). There are three ways to restyle it, from the most recommended to the most surgical.
368
+
369
+ > **No inline styles.** LayerPro never writes a plain inline style on the elements it renders. Every declaration lives in the stylesheet and is driven by a **class** (the state) or by a **CSS custom property** (the value). That means you can restyle any part of a window with a plain CSS rule - nothing needs `!important` to win over an inline style.
370
+
371
+ ### 1. CSS custom properties (recommended)
372
+
373
+ Every visual detail is driven by a CSS variable declared on `:root`. Override the variable and every popup updates - no selectors, no `!important`:
374
+
375
+ ```css
376
+ :root {
377
+ --bar-background: #1f2937;
378
+ --bar-selected-background: #2563eb;
379
+ --bar-color: #f9fafb;
380
+ --window-background: #111827;
381
+ --window-border-radius: 4px;
382
+ --internal-background: #0b1220;
383
+ --body-color: #e5e7eb;
384
+ --button-background-color: #2563eb;
385
+ --button-color: #ffffff;
386
+ }
387
+ ```
388
+
389
+ Variables can also be scoped per popup subtree, because each window is rendered inside `#popup`:
390
+
391
+ ```css
392
+ #popup .popup_window_myDarkPopup {
393
+ --window-background: #000;
394
+ --bar-background: #000;
395
+ }
396
+ ```
397
+
398
+ ### 2. Target the generated classes
399
+
400
+ Every element LayerPro creates carries a **stable class alias** next to its id-suffixed class, so you can target windows, bars, bodies, and buttons without depending on generated ids:
401
+
402
+ | Element | Stable class | Id-suffixed class |
403
+ |---------|--------------|-------------------|
404
+ | Window surface | `.popup_window` | `.popup_window_<id>` |
405
+ | Title bar | `.popup_bar` | `.popup_bar_<id>` |
406
+ | Drag handle (title) | `.popup_drag` | `.popup_drag_<id>` |
407
+ | Icon slot | `.popup_icon` | `.popup_icon_<id>` |
408
+ | Option buttons | `.popup_option` | `.popup_option_<id>` |
409
+ | Internal area | `.popup_internal` | `.popup_internal_<id>` |
410
+ | Body | `.popup_body` | `.popup_body_<id>` |
411
+ | Modal overlay | `.popup_modal` | `.popup_modal_<id>` |
412
+ | Positioned wrapper | `.popup_overflow` | `.popup_overflow_<id>` |
413
+ | Confirm/cancel row | `.buttons` | - |
414
+ | Selected/focused window | `.zHigh`, `.popup_bar_selected` | - |
415
+
416
+ ```css
417
+ /* square windows, no rounded corners */
418
+ #popup .popup_window {
419
+ border-radius: 0;
420
+ }
421
+
422
+ /* restyle the confirm/cancel row */
423
+ #popup .buttons button {
424
+ border-radius: 6px;
425
+ background: #2563eb;
426
+ }
427
+
428
+ /* make the close button red on hover */
429
+ #popup .popup_option .close:hover {
430
+ background: #dc2626;
431
+ }
432
+
433
+ /* change the dock strip */
434
+ #popup_dock {
435
+ background: #111827;
436
+ }
437
+ ```
438
+
439
+ > **Note:** some internal rules use `!important` (for example the active-window border and the built-in icons). For those, use the cascade layer approach below instead of `!important`.
440
+
441
+ ### State classes
442
+
443
+ The visual **states** of a window are classes too, so you can restyle them like any other element:
444
+
445
+ | Class | Applied when | Effect |
446
+ |-------|--------------|--------|
447
+ | `.popup_maximized` | the window is maximized | fills the viewport above the dock |
448
+ | `.popup_iconized` | the window is minimized to the dock | collapsed on the taskbar |
449
+ | `.popup_dimmed` | another window has the focus | pushed to the back (opacity `.5`) |
450
+ | `.popup_content_hidden` | another window has the focus | body content not rendered |
451
+ | `.popup_content_visible` | the window has the focus | body content always rendered |
452
+ | `.lp_hidden` | the element is hidden | `display: none` |
453
+ | `.lp_fade_in` / `.lp_fade_out` | during a fade | opacity transition |
454
+ | `.lp_no_pointer` | while dragging/resizing | `pointer-events: none` |
455
+ | `.lp_no_touch` | on the drag/resize handles | `touch-action: none` |
456
+ | `.lp_no_transition` | while resizing | transitions disabled |
457
+
458
+ ```css
459
+ /* a maximized window with a different look */
460
+ #popup .popup_maximized {
461
+ inset: 0;
462
+ bottom: 0;
463
+ }
464
+
465
+ /* a dimmed window that stays readable */
466
+ #popup .popup_dimmed {
467
+ opacity: .8;
468
+ }
469
+ ```
470
+
471
+ The geometry of a window (position and size) is carried by the custom properties below, so it can be overridden too:
472
+
473
+ | Property | Value |
474
+ |----------|-------|
475
+ | `--lp-top` / `--lp-left` | window position |
476
+ | `--lp-right` / `--lp-bottom` | window position (alternative anchors) |
477
+ | `--lp-width` / `--lp-height` | window size |
478
+ | `--lp-min-width` / `--lp-min-height` | minimum size |
479
+ | `--lp-max-width` / `--lp-max-height` | maximum size |
480
+ | `--lp-z-index` | stacking order |
481
+
482
+ ```css
483
+ /* every window opens at a fixed position */
484
+ #popup .popup_overflow {
485
+ --lp-left: 50%;
486
+ --lp-top: 100px;
487
+ }
488
+ ```
489
+
490
+ ### 3. Cascade layers (full control)
491
+
492
+ All LayerPro styles live inside the CSS cascade layer `layerpro`, so a host app can restyle the popups without `!important` or fragile selectors.
493
+
494
+ ```css
495
+ /* host app: plain rules beat every layer */
496
+ #popup { border-radius: 4px; }
497
+ ```
498
+
499
+ Precedence rules:
500
+
501
+ - **Unlayered host rules always win** over anything inside `layerpro`.
502
+ - **Host layers must be declared before `layerpro`** to outrank it. Declare the
503
+ full order in the host stylesheet, otherwise the host layer is created after
504
+ LayerPro's and loses:
505
+
506
+ ```css
507
+ @layer app, layerpro;
508
+ ```
509
+
510
+ - **`!important` inverts layer order.** A host `!important` rule is *weaker*
511
+ than LayerPro's `!important` rules, because LayerPro's live inside a layer
512
+ and host `!important` rules are unlayered. Do not rely on `!important` to
513
+ override LayerPro - use plain rules or a host layer declared before
514
+ `layerpro`.
515
+
516
+ ## Dock
517
+
518
+ Popups that can be minimized share a dock, a taskbar-like strip pinned to the bottom of the viewport. The dock is mounted directly in `<body>` and it stays visible while the page scrolls.
519
+
520
+ While the dock is mounted LayerPro puts the `layerpro-dock` class on `<html>` and `<body>`:
521
+
522
+ ```css
523
+ html.layerpro-dock { height: 100%; overflow: hidden; }
524
+ body.layerpro-dock { position: absolute; top: 0; right: 0; bottom: var(--dock-height); left: 0; overflow-y: auto; }
525
+ ```
526
+
527
+ The body is lifted from the bottom, so the strip used by the dock stays free and the page content is never covered by it. The class is removed as soon as the last popup is closed, so the host page is left exactly as it was. No inline style is written on the host page.
528
+
529
+ > **Note:** while the dock is open the body is the scrolling element, so `window.scrollY` stays `0` and page level scroll listeners do not fire. Host the scrolling in an inner container if the page needs it.
530
+
301
531
  ## Security
302
532
 
303
533
  - **Same-origin fetch guard** - the `source` prop only accepts same-origin URLs; cross-origin requests are rejected before any network call is made, preventing SSRF and data exfiltration.