layerpro 4.0.0 → 4.1.0
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 +290 -16
- package/global.cjs +783 -551
- package/global.js +783 -551
- package/index.cjs +783 -551
- package/index.css +619 -502
- package/index.js +783 -551
- package/llms.txt +15 -6
- package/markdown/GUIDE.md +233 -12
- package/package.json +1 -1
- package/types/layerpro.d.ts +78 -5
package/README.md
CHANGED
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|

|
|
19
19
|

|
|
20
20
|
|
|
21
|
-
LayerPro
|
|
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,20 @@ 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)
|
|
38
|
+
- [Targeting a window](#targeting-a-window)
|
|
33
39
|
- [Options](#options)
|
|
34
40
|
- [Global Overrides](#global-overrides)
|
|
35
41
|
- [Methods](#methods)
|
|
36
42
|
- [Styling & z-index](#styling--z-index)
|
|
43
|
+
- [Overriding the CSS](#overriding-the-css)
|
|
44
|
+
- [CSS custom properties](#1-css-custom-properties-recommended)
|
|
45
|
+
- [Target the generated classes](#2-target-the-generated-classes)
|
|
46
|
+
- [Cascade layers](#3-cascade-layers-full-control)
|
|
47
|
+
- [Dock](#dock)
|
|
37
48
|
- [Security](#security)
|
|
38
|
-
- [Browser Support](#browser-support)
|
|
39
|
-
- [Contributing](#contributing)
|
|
40
49
|
- [License](#license)
|
|
41
50
|
|
|
42
51
|
---
|
|
@@ -44,7 +53,10 @@ LayerPro replaces the browser's native `alert()`, `confirm()`, and `prompt()` wi
|
|
|
44
53
|
## Features
|
|
45
54
|
|
|
46
55
|
- **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
|
-
- **
|
|
56
|
+
- **Native `<dialog>` windows** - every popup is a real `<dialog>` element, so the platform owns the window semantics (open state, implicit dialog role) while LayerPro keeps the geometry, the drag and the stacking order
|
|
57
|
+
- **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
|
|
58
|
+
- **Hosts any content** - plain text, HTML strings, or **full React components** (with Context bridging and DOM auto-healing), rendered inside the window body
|
|
59
|
+
- **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
60
|
- **Fully customizable** - appearance, behavior, and content
|
|
49
61
|
- **Dockable windows** (taskbar-style) - click a dock icon to restore, focus, or minimize, just like an OS taskbar
|
|
50
62
|
- **Draggable & resizable** - instant, with no transition lag while resizing
|
|
@@ -72,7 +84,7 @@ Import LayerPro once, in your app's entry point:
|
|
|
72
84
|
import "layerpro";
|
|
73
85
|
```
|
|
74
86
|
|
|
75
|
-
That's it - `alert()`, `confirm()`, `prompt()`, `message()
|
|
87
|
+
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
88
|
|
|
77
89
|
The `popup` alias is a shorthand for `layerpro.popup`:
|
|
78
90
|
|
|
@@ -89,19 +101,15 @@ popup.open({ id: 'myPopup', body: 'Hello World' });
|
|
|
89
101
|
### Basic Modals
|
|
90
102
|
|
|
91
103
|
```javascript
|
|
92
|
-
alert("Hello World!");
|
|
93
|
-
|
|
94
|
-
confirm("Are you sure?", () => {
|
|
95
|
-
console.debug("User clicked YES");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
prompt("What's your name?", (name) => {
|
|
99
|
-
console.debug("User entered:", name);
|
|
100
|
-
});
|
|
104
|
+
layerpro.alert("Hello World!"); // Promise<void>
|
|
105
|
+
layerpro.message("Operation complete!"); // Promise<void>
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
const confirmed = await layerpro.confirm("Are you sure?"); // Promise<boolean>
|
|
108
|
+
const name = await layerpro.prompt("What's your name?"); // Promise<string>
|
|
103
109
|
```
|
|
104
110
|
|
|
111
|
+
> **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.
|
|
112
|
+
|
|
105
113
|
### Custom Popups
|
|
106
114
|
|
|
107
115
|
```javascript
|
|
@@ -118,6 +126,48 @@ popup.open({
|
|
|
118
126
|
});
|
|
119
127
|
```
|
|
120
128
|
|
|
129
|
+
### OS-style Windows
|
|
130
|
+
|
|
131
|
+
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.
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
popup.open({
|
|
135
|
+
id: 'dashboard',
|
|
136
|
+
name: 'Dashboard',
|
|
137
|
+
icon: '📊',
|
|
138
|
+
body: '<h2>Live metrics</h2><p>Anything you want inside.</p>',
|
|
139
|
+
width: 640,
|
|
140
|
+
height: 420,
|
|
141
|
+
movable: true,
|
|
142
|
+
resizable: true,
|
|
143
|
+
maximize: true,
|
|
144
|
+
iconize: true,
|
|
145
|
+
dockable: true,
|
|
146
|
+
close: true
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
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:
|
|
151
|
+
|
|
152
|
+
```jsx
|
|
153
|
+
popup.open({
|
|
154
|
+
id: 'settingsWindow',
|
|
155
|
+
name: 'Settings',
|
|
156
|
+
icon: '⚙',
|
|
157
|
+
body: <SettingsPanel />,
|
|
158
|
+
width: 720,
|
|
159
|
+
height: 480,
|
|
160
|
+
movable: true,
|
|
161
|
+
resizable: true,
|
|
162
|
+
maximize: true,
|
|
163
|
+
iconize: true,
|
|
164
|
+
dockable: true,
|
|
165
|
+
close: true
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
> **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.
|
|
170
|
+
|
|
121
171
|
### React Components
|
|
122
172
|
|
|
123
173
|
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.
|
|
@@ -167,6 +217,33 @@ popup.open({
|
|
|
167
217
|
|
|
168
218
|
> **Note**: in React, always use elements (`<Component />`), not function calls (`Component()`).
|
|
169
219
|
|
|
220
|
+
### Targeting a window
|
|
221
|
+
|
|
222
|
+
A layer is drawn in the document of the page that loaded LayerPro. The
|
|
223
|
+
`target` option names the window a layer belongs to, so a window opened with
|
|
224
|
+
`native: true` can host its own layers instead of seeing them land on the
|
|
225
|
+
opener:
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Open a real browser window
|
|
229
|
+
await layerpro.popup.open({ id: 'desk', name: 'Desktop', native: true })
|
|
230
|
+
|
|
231
|
+
// Open a layer inside that window
|
|
232
|
+
await layerpro.popup.open({ id: 'detail', body: 'Inside the desk', target: 'desk' })
|
|
233
|
+
|
|
234
|
+
// The same thing, through the handle bound to that window
|
|
235
|
+
const desk = layerpro.window('desk')
|
|
236
|
+
await desk.popup.open({ id: 'detail2', body: 'Also inside' })
|
|
237
|
+
|
|
238
|
+
// The modals accept a target too
|
|
239
|
+
await layerpro.confirm('Save the changes?', { target: 'desk' })
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
A target is the id of a native window, a `Window` object, or a
|
|
243
|
+
`{ win, doc }` scope. A target that cannot be reached (unknown id, closed
|
|
244
|
+
window, another origin) produces a warning and the layer is drawn in the
|
|
245
|
+
current document.
|
|
246
|
+
|
|
170
247
|
## Options
|
|
171
248
|
|
|
172
249
|
```typescript
|
|
@@ -218,12 +295,17 @@ popup.open({
|
|
|
218
295
|
confirm?: { text: string; cb?: () => void },
|
|
219
296
|
cancel?: { text: string; cb?: () => void }
|
|
220
297
|
}
|
|
298
|
+
|
|
299
|
+
// The window the layer is drawn in: the id of a native window, a Window
|
|
300
|
+
// object, or a { win, doc } scope. Without it the layer is drawn in the
|
|
301
|
+
// document of the page that loaded LayerPro.
|
|
302
|
+
target?: string | number | Window | LayerScope
|
|
221
303
|
}
|
|
222
304
|
```
|
|
223
305
|
|
|
224
306
|
> **`modal`** injects a full-viewport overlay (`popup_modal_<id>`) that intercepts every pointer event - the page underneath becomes unclickable until the popup closes. Use `modal: true` for alert/confirm/prompt/message pop ups.
|
|
225
307
|
|
|
226
|
-
> **Duplicate id** - opening a popup whose `id` already exists brings that window to the front and refreshes its `body`/`buttons` instead of doing nothing.
|
|
308
|
+
> **Duplicate id** - opening a popup whose `id` already exists brings that window to the front and refreshes its `body`/`buttons` instead of doing nothing. The refresh reuses the existing button row: the row is emptied and refilled, never stacked.
|
|
227
309
|
|
|
228
310
|
## Global Overrides (Opt-in in 4.0)
|
|
229
311
|
|
|
@@ -273,6 +355,8 @@ const val = await prompt("Name?"); // Promise<string>
|
|
|
273
355
|
| `popup.movable(props)` | Enable dragging. |
|
|
274
356
|
| `popup.resizable(props)` | Enable resizing. |
|
|
275
357
|
| `layerpro.installGlobals()` | Override `window.alert/confirm/prompt/message` with LayerPro's Promise-based versions. |
|
|
358
|
+
| `layerpro.window(target)` | A handle bound to one window: `win`, `doc` and a `popup` API whose layers are drawn in that window. |
|
|
359
|
+
| `layerpro.version()` | Returns the current LayerPro version string (read from `app.json`). |
|
|
276
360
|
| `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
361
|
|
|
278
362
|
## Styling & z-index
|
|
@@ -298,6 +382,196 @@ The title bar, window surface, internal area, and body all read from CSS custom
|
|
|
298
382
|
| `--modal-background` | modal overlay |
|
|
299
383
|
| `--popup-z-index` | base z-index for the stack |
|
|
300
384
|
|
|
385
|
+
Additional variables available for buttons, inputs, resize handles, and the dock:
|
|
386
|
+
|
|
387
|
+
| Variable | Applies to |
|
|
388
|
+
|----------|-----------|
|
|
389
|
+
| `--button-background-color` / `--button-color` | confirm/cancel buttons |
|
|
390
|
+
| `--input-background-color` / `--input-color` | prompt input field |
|
|
391
|
+
| `--inputButton-fontSize` | button/input font size |
|
|
392
|
+
| `--resize-background-color` / `--resize-shadow` | resize handles |
|
|
393
|
+
| `--dock-background-color` / `--dock-height` | taskbar strip |
|
|
394
|
+
| `--dock-sideLeft` / `--dock-sideCenter` / `--dock-sideRight` | dock alignment |
|
|
395
|
+
| `--dock-icon-width` | dock icon width |
|
|
396
|
+
| `--transition-time` | fade/hover transitions |
|
|
397
|
+
| `--border-radius` / `--corner-size` / `--corner-border-radius` | resize handle shape |
|
|
398
|
+
| `--hover-opacity` / `--hover-filter` | hover feedback |
|
|
399
|
+
|
|
400
|
+
## Overriding the CSS
|
|
401
|
+
|
|
402
|
+
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.
|
|
403
|
+
|
|
404
|
+
> **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.
|
|
405
|
+
|
|
406
|
+
### 1. CSS custom properties (recommended)
|
|
407
|
+
|
|
408
|
+
Every visual detail is driven by a CSS variable declared on `:root`. Override the variable and every popup updates - no selectors, no `!important`:
|
|
409
|
+
|
|
410
|
+
```css
|
|
411
|
+
:root {
|
|
412
|
+
--bar-background: #1f2937;
|
|
413
|
+
--bar-selected-background: #2563eb;
|
|
414
|
+
--bar-color: #f9fafb;
|
|
415
|
+
--window-background: #111827;
|
|
416
|
+
--window-border-radius: 4px;
|
|
417
|
+
--internal-background: #0b1220;
|
|
418
|
+
--body-color: #e5e7eb;
|
|
419
|
+
--button-background-color: #2563eb;
|
|
420
|
+
--button-color: #ffffff;
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Variables can also be scoped per popup subtree, because each window is rendered inside `#popup`:
|
|
425
|
+
|
|
426
|
+
```css
|
|
427
|
+
#popup .popup_window_myDarkPopup {
|
|
428
|
+
--window-background: #000;
|
|
429
|
+
--bar-background: #000;
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 2. Target the generated classes
|
|
434
|
+
|
|
435
|
+
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:
|
|
436
|
+
|
|
437
|
+
| Element | Stable class | Id-suffixed class |
|
|
438
|
+
|---------|--------------|-------------------|
|
|
439
|
+
| Window surface (`<dialog>`) | `.popup_window` | `.popup_window_<id>` |
|
|
440
|
+
| Title bar | `.popup_bar` | `.popup_bar_<id>` |
|
|
441
|
+
| Drag handle (title) | `.popup_drag` | `.popup_drag_<id>` |
|
|
442
|
+
| Icon slot | `.popup_icon` | `.popup_icon_<id>` |
|
|
443
|
+
| Option buttons | `.popup_option` | `.popup_option_<id>` |
|
|
444
|
+
| Internal area | `.popup_internal` | `.popup_internal_<id>` |
|
|
445
|
+
| Body | `.popup_body` | `.popup_body_<id>` |
|
|
446
|
+
| Modal overlay | `.popup_modal` | `.popup_modal_<id>` |
|
|
447
|
+
| Positioned wrapper | `.popup_overflow` | `.popup_overflow_<id>` |
|
|
448
|
+
| Confirm/cancel row | `.buttons` | - |
|
|
449
|
+
| Selected/focused window | `.zHigh`, `.popup_bar_selected` | - |
|
|
450
|
+
|
|
451
|
+
The confirm/cancel row built from the `buttons` option lives in the **internal
|
|
452
|
+
area** (`.popup_internal`), next to the body, and not inside the body itself:
|
|
453
|
+
a body renderer (React) owns the body container and empties it when it
|
|
454
|
+
commits, so a row kept inside the body would be wiped by the first render.
|
|
455
|
+
The built-in `alert`/`confirm`/`prompt`/`message` modals carry their own row
|
|
456
|
+
inside their body markup, as part of the modal content. Target the row with
|
|
457
|
+
`.buttons` (or `.lp_confirm` / `.lp_cancel` for the single buttons), never
|
|
458
|
+
with `.popup_body .buttons`.
|
|
459
|
+
|
|
460
|
+
```css
|
|
461
|
+
/* square windows, no rounded corners */
|
|
462
|
+
#popup .popup_window {
|
|
463
|
+
border-radius: 0;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* restyle the confirm/cancel row */
|
|
467
|
+
#popup .buttons button {
|
|
468
|
+
border-radius: 6px;
|
|
469
|
+
background: #2563eb;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/* make the close button red on hover */
|
|
473
|
+
#popup .popup_option .close:hover {
|
|
474
|
+
background: #dc2626;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* change the dock strip */
|
|
478
|
+
#popup_dock {
|
|
479
|
+
background: #111827;
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
> **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`.
|
|
484
|
+
|
|
485
|
+
### State classes
|
|
486
|
+
|
|
487
|
+
The visual **states** of a window are classes too, so you can restyle them like any other element:
|
|
488
|
+
|
|
489
|
+
| Class | Applied when | Effect |
|
|
490
|
+
|-------|--------------|--------|
|
|
491
|
+
| `.popup_maximized` | the window is maximized | fills the viewport above the dock |
|
|
492
|
+
| `.popup_iconized` | the window is minimized to the dock | collapsed on the taskbar |
|
|
493
|
+
| `.popup_dimmed` | another window has the focus | pushed to the back (opacity `.5`) |
|
|
494
|
+
| `.popup_content_hidden` | another window has the focus | body content not rendered |
|
|
495
|
+
| `.popup_content_visible` | the window has the focus | body content always rendered |
|
|
496
|
+
| `.lp_hidden` | the element is hidden | `display: none` |
|
|
497
|
+
| `.lp_fade_in` / `.lp_fade_out` | during a fade | opacity transition |
|
|
498
|
+
| `.lp_no_pointer` | while dragging/resizing | `pointer-events: none` |
|
|
499
|
+
| `.lp_no_touch` | on the drag/resize handles | `touch-action: none` |
|
|
500
|
+
| `.lp_no_transition` | while resizing | transitions disabled |
|
|
501
|
+
|
|
502
|
+
```css
|
|
503
|
+
/* a maximized window with a different look */
|
|
504
|
+
#popup .popup_maximized {
|
|
505
|
+
inset: 0;
|
|
506
|
+
bottom: 0;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* a dimmed window that stays readable */
|
|
510
|
+
#popup .popup_dimmed {
|
|
511
|
+
opacity: .8;
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
The geometry of a window (position and size) is carried by the custom properties below, so it can be overridden too:
|
|
516
|
+
|
|
517
|
+
| Property | Value |
|
|
518
|
+
|----------|-------|
|
|
519
|
+
| `--lp-top` / `--lp-left` | window position |
|
|
520
|
+
| `--lp-right` / `--lp-bottom` | window position (alternative anchors) |
|
|
521
|
+
| `--lp-width` / `--lp-height` | window size |
|
|
522
|
+
| `--lp-min-width` / `--lp-min-height` | minimum size |
|
|
523
|
+
| `--lp-max-width` / `--lp-max-height` | maximum size |
|
|
524
|
+
| `--lp-z-index` | stacking order |
|
|
525
|
+
|
|
526
|
+
```css
|
|
527
|
+
/* every window opens at a fixed position */
|
|
528
|
+
#popup .popup_overflow {
|
|
529
|
+
--lp-left: 50%;
|
|
530
|
+
--lp-top: 100px;
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### 3. Cascade layers (full control)
|
|
535
|
+
|
|
536
|
+
All LayerPro styles live inside the CSS cascade layer `layerpro`, so a host app can restyle the popups without `!important` or fragile selectors.
|
|
537
|
+
|
|
538
|
+
```css
|
|
539
|
+
/* host app: plain rules beat every layer */
|
|
540
|
+
#popup { border-radius: 4px; }
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Precedence rules:
|
|
544
|
+
|
|
545
|
+
- **Unlayered host rules always win** over anything inside `layerpro`.
|
|
546
|
+
- **Host layers must be declared before `layerpro`** to outrank it. Declare the
|
|
547
|
+
full order in the host stylesheet, otherwise the host layer is created after
|
|
548
|
+
LayerPro's and loses:
|
|
549
|
+
|
|
550
|
+
```css
|
|
551
|
+
@layer app, layerpro;
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
- **`!important` inverts layer order.** A host `!important` rule is *weaker*
|
|
555
|
+
than LayerPro's `!important` rules, because LayerPro's live inside a layer
|
|
556
|
+
and host `!important` rules are unlayered. Do not rely on `!important` to
|
|
557
|
+
override LayerPro - use plain rules or a host layer declared before
|
|
558
|
+
`layerpro`.
|
|
559
|
+
|
|
560
|
+
## Dock
|
|
561
|
+
|
|
562
|
+
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.
|
|
563
|
+
|
|
564
|
+
While the dock is mounted LayerPro puts the `layerpro-dock` class on `<html>` and `<body>`:
|
|
565
|
+
|
|
566
|
+
```css
|
|
567
|
+
html.layerpro-dock { height: 100%; overflow: hidden; }
|
|
568
|
+
body.layerpro-dock { position: absolute; top: 0; right: 0; bottom: var(--dock-height); left: 0; overflow-y: auto; }
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
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.
|
|
572
|
+
|
|
573
|
+
> **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.
|
|
574
|
+
|
|
301
575
|
## Security
|
|
302
576
|
|
|
303
577
|
- **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.
|