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/llms.txt CHANGED
@@ -1,22 +1,28 @@
1
1
  # LayerPro
2
2
 
3
- > Dependency-free custom modals (alert, confirm, prompt, message) and advanced
4
- > popups for the browser. Vanilla DOM (no jQuery); React/react-dom are external
5
- > and used only to render the modal body. Dockable, draggable, resizable,
6
- > maximizable (pure CSS), with an OS-style taskbar.
3
+ > Browser window system: OS-style windows (draggable, resizable, maximizable,
4
+ > dockable) that host any content, including React components. Also provides
5
+ > Promise-based custom modals (alert, confirm, prompt, message) as a drop-in
6
+ > replacement for the browser's native ones. Vanilla DOM (no jQuery);
7
+ > React/react-dom are external and used only to render the modal body.
8
+ > Dockable, draggable, resizable, maximizable (pure CSS), with an OS-style
9
+ > taskbar. Fully themeable via CSS custom properties or the `layerpro` cascade
10
+ > layer - see "Overriding the CSS" in the README.
7
11
 
8
12
  ## Source
9
13
 
10
14
  - [index.ts](index.ts): Entry point. Sets `window.layerpro` (frozen, non-enumerable), imports SCSS, initializes mode.
11
- - [core/dom.ts](core/dom.ts): jQuery-free chainable DOM helper (`LpEl`, `lp`, `lpAll`, `lpCreate`, `lpDoc`, `lpWin`) - the replacement for jQuery.
15
+ - [core/dom.ts](core/dom.ts): jQuery-free chainable DOM helper (`LpEl`, `lp`, `lpAll`, `lpCreate`, `lpDoc`, `lpWin`) - the replacement for jQuery. Document aware: elements are created and resolved in the document of the scope they belong to.
16
+ - [core/scope.ts](core/scope.ts): The scope of a layer - the (window, document) pair it is drawn in. Resolves a target (native window id, Window, scope) into a scope and keeps the scope of every open layer.
12
17
  - [core/properties.ts](core/properties.ts): The `popup` object - `open`, `close`, `center`, `maximize`, `iconize`, `index`, `zIndex`, `movable`, `resizable`, `cleanupEvents`, `unmount`.
13
- - [core/genLayer.ts](core/genLayer.ts): DOM builder for a popup, dock/buttons wiring, pointer/click/dblclick handlers.
18
+ - [core/genLayer.ts](core/genLayer.ts): DOM builder for a popup, dock/buttons wiring, pointer/click/dblclick handlers. The window surface is a native `<dialog>`, shown with `show()`; the confirm/cancel row lives in `.popup_internal` (not in the body), so a React body cannot wipe it.
14
19
  - [core/accessibility.ts](core/accessibility.ts): ARIA attributes, focus save/restore, Tab focus trap.
15
20
  - [core/message.ts](core/message.ts): `alert`/`confirm`/`message`/`prompt` - Promise-based (resolve on close); in 4.0 globals are opt-in via `layerpro/global`, `noGlobals` is removed.
16
21
  - [core/mode.ts](core/mode.ts): Close popups on orientation/size mode switch.
17
22
  - [core/mobile.ts](core/mobile.ts): Unified pointer/mouse/touch events (`getPointerDown`/`getPointerMove`/`getPointerUp`).
18
23
  - [core/window.ts](core/window.ts): Window-level helpers (scroll, size).
19
24
  - [core/credits.ts](core/credits.ts): Credits overlay helper.
25
+ - [core/version.ts](core/version.ts): `layerpro.version()` - reads the current version from `app.json`.
20
26
  - [core/namespace.ts](core/namespace.ts): Global namespace setup.
21
27
 
22
28
  ## Types
@@ -40,6 +46,9 @@
40
46
  ## Key behaviors
41
47
 
42
48
  - **jQuery removed** - all DOM via `core/dom.ts`; public API unchanged.
49
+ - **Windows are native `<dialog>` elements** - shown with `show()` (not `showModal()`), so the element stays in the normal flow and the LayerPro geometry, drag, resize and stacking order are unchanged. The UA look (canvas background, padding, border, auto margin) is reset by the stylesheet.
50
+ - **The button row is outside the body** - the `buttons` option renders its row in `.popup_internal`, next to the body, and reuses it on every re-open. React owns the body container and empties it on commit, so a row inside the body would be wiped.
51
+ - **Target window** - `props.target` names the window a layer is drawn in (native window id, Window, or scope); `layerpro.window(target)` returns a handle bound to that window; the modals accept `{ target }`. Without a target a layer is drawn in the document of the realm that loaded LayerPro.
43
52
  - **React external** - only the modal `body` is rendered with `react-dom` `createRoot`.
44
53
  - **Instant resize** - CSS `transition` disabled on the popup while resizing (no lag).
45
54
  - **Maximize is pure CSS** - `position: fixed` + insets; follows viewport with no JS listeners.
package/markdown/GUIDE.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # LayerPro Guide
2
2
 
3
- > A practical, example-driven guide to LayerPro - the real custom modals
4
- > (alert, confirm, prompt, message) and advanced popups for the browser.
3
+ > A practical, example-driven guide to LayerPro - a browser window system that
4
+ > generates OS-style windows (draggable, resizable, maximizable, dockable) able
5
+ > to host any content, including React components, and that also provides
6
+ > Promise-based custom modals (alert, confirm, prompt, message) as a drop-in
7
+ > replacement for the browser's native ones.
5
8
 
6
9
  LayerPro is **dependency-free**: it no longer uses jQuery. All DOM work is done
7
10
  with small, chainable vanilla helpers. React and react-dom are the only
@@ -16,14 +19,17 @@ body when you pass a React element.
16
19
  2. [Quick Start](#quick-start)
17
20
  3. [Basic Modals](#basic-modals)
18
21
  4. [Custom Popups](#custom-popups)
19
- 5. [Options Reference](#options-reference)
20
- 6. [Dock & Taskbar](#dock--taskbar)
21
- 7. [Maximize & Minimize](#maximize--minimize)
22
- 8. [Dragging & Resizing](#dragging--resizing)
23
- 9. [React Body](#react-body)
24
- 10. [Selection & z-index](#selection--z-index)
25
- 11. [API Methods](#api-methods)
26
- 12. [Notes & Migration](#notes--migration)
22
+ 5. [OS-style Windows](#os-style-windows)
23
+ 6. [Targeting a window](#targeting-a-window)
24
+ 7. [Options Reference](#options-reference)
25
+ 8. [Dock & Taskbar](#dock--taskbar)
26
+ 9. [Maximize & Minimize](#maximize--minimize)
27
+ 10. [Dragging & Resizing](#dragging--resizing)
28
+ 11. [React Body](#react-body)
29
+ 12. [Selection & z-index](#selection--z-index)
30
+ 13. [API Methods](#api-methods)
31
+ 14. [Styling & z-index](#styling--z-index)
32
+ 15. [Notes & Migration](#notes--migration)
27
33
 
28
34
  ---
29
35
 
@@ -129,6 +135,100 @@ layerpro.popup.open({
129
135
 
130
136
  ---
131
137
 
138
+ ## OS-style Windows
139
+
140
+ A popup is not limited to a modal dialog. With `movable`, `resizable`,
141
+ `maximize`, `iconize` and `dockable` enabled it becomes a real OS-style
142
+ window: draggable by its title bar, resizable from the edges, maximizable,
143
+ minimizable into the dock, and stackable with the other windows on the page.
144
+
145
+ ```javascript
146
+ layerpro.popup.open({
147
+ id: "dashboard",
148
+ name: "Dashboard",
149
+ icon: "&#128202;",
150
+ body: "<h2>Live metrics</h2><p>Anything you want inside.</p>",
151
+ width: 640,
152
+ height: 420,
153
+ movable: true,
154
+ resizable: true,
155
+ maximize: true,
156
+ iconize: true,
157
+ dockable: true,
158
+ close: true
159
+ });
160
+ ```
161
+
162
+ With a React component inside, this becomes a real application window rendered
163
+ in the page with native-looking chrome:
164
+
165
+ ```jsx
166
+ layerpro.popup.open({
167
+ id: "settingsWindow",
168
+ name: "Settings",
169
+ icon: "&#9881;",
170
+ body: <SettingsPanel />,
171
+ width: 720,
172
+ height: 480,
173
+ movable: true,
174
+ resizable: true,
175
+ maximize: true,
176
+ iconize: true,
177
+ dockable: true,
178
+ close: true
179
+ });
180
+ ```
181
+
182
+ > `modal: true` is what turns a window into a blocking dialog. Leave it off for
183
+ > windows the user should be able to move around while working on the page.
184
+
185
+ ---
186
+
187
+ ## Targeting a window
188
+
189
+ Every layer belongs to one window: the document it is drawn in. By default
190
+ that is the document of the page that loaded LayerPro. The `target` option
191
+ names another window, so a window opened with `native: true` can host its
192
+ own layers instead of seeing them land on the opener.
193
+
194
+ ```javascript
195
+ // A real browser window
196
+ await layerpro.popup.open({
197
+ id: "desk",
198
+ name: "Desktop",
199
+ native: true
200
+ });
201
+
202
+ // A layer inside that window
203
+ await layerpro.popup.open({
204
+ id: "detail",
205
+ body: "<p>Inside the desk</p>",
206
+ target: "desk"
207
+ });
208
+
209
+ // The handle bound to that window does the same
210
+ const desk = layerpro.window("desk");
211
+ await desk.popup.open({ id: "detail2", body: "<p>Also inside</p>" });
212
+
213
+ // The modals accept a target too
214
+ await layerpro.confirm("Save the changes?", { target: "desk" });
215
+ ```
216
+
217
+ A target is one of:
218
+
219
+ | Target | Meaning |
220
+ |--------|---------|
221
+ | `string` / `number` | the id of a native window opened by LayerPro |
222
+ | `Window` | that browser window, when it is reachable (same origin) |
223
+ | `{ win, doc }` | an explicit scope |
224
+
225
+ The target window carries the LayerPro stylesheet, so a layer drawn there
226
+ looks exactly like a layer drawn on the page. A target that cannot be
227
+ reached (unknown id, closed window, another origin) produces a warning and
228
+ the layer is drawn in the current document.
229
+
230
+ ---
231
+
132
232
  ## Options Reference
133
233
 
134
234
  ```typescript
@@ -169,8 +269,13 @@ layerpro.popup.open({
169
269
  dockable?: boolean, // show an icon in the dock bar
170
270
  close?: boolean, // show the close button
171
271
  modal?: boolean, // show a full-screen overlay that blocks pointer events beneath
172
- }
173
- ```
272
+
273
+ // The window the layer is drawn in: the id of a native window, a Window
274
+ // object, or a { win, doc } scope. Without it the layer is drawn in the
275
+ // document of the page that loaded LayerPro.
276
+ target?: string | number | Window | LayerScope,
277
+ }
278
+ ```
174
279
 
175
280
  If `top/left/right/bottom` are all `0` (or unset) the popup is auto-centered.
176
281
  Pass `center: true` to force centering regardless of the position props.
@@ -280,6 +385,11 @@ layerpro.popup.open({
280
385
 
281
386
  React state, effects, and context all work normally inside the body.
282
387
 
388
+ The body container belongs to React: React empties it when it commits the first
389
+ render. Anything LayerPro needs to keep inside the window is therefore drawn in
390
+ the internal area (`.popup_internal`), next to the body - this is where the
391
+ confirm/cancel row lives, so a React body can never wipe it.
392
+
283
393
  ---
284
394
 
285
395
  ## Selection & z-index
@@ -312,6 +422,7 @@ layerpro.popup.index({ id: "myModal" });
312
422
  | `layerpro.popup.cleanupEvents(id)` | Remove dock/button/esc listeners |
313
423
  | `layerpro.popup.unmount(id)` | Unmount the React root (memory cleanup) |
314
424
  | `layerpro.config(opts)` | Runtime config. Supports `{ nonce: string }` for CSP; sets `window.__layerproNonce`. Call before importing LayerPro for the nonce to be present at CSS injection time |
425
+ | `layerpro.window(target)` | A handle bound to one window: `win`, `doc` and a `popup` API whose layers are drawn in that window |
315
426
  | `layerpro.installGlobals()` | Explicitly install global `alert`/`confirm`/`prompt`/`message` overrides (opt-in in 4.0) |
316
427
 
317
428
  ---
@@ -347,6 +458,116 @@ Stable class names (`.popup_bar`, `.popup_window`, `.popup_overflow`,
347
458
  `.popup_modal`) are emitted alongside the per-id classes so themes can target
348
459
  elements without attribute selectors.
349
460
 
461
+ The window surface itself is a native `<dialog>` element: it is shown with
462
+ `show()` (not `showModal()`), so it stays in the normal flow and the LayerPro
463
+ geometry and stacking order keep working. The native look of the element
464
+ (canvas background, padding, border, auto margin) is reset by the LayerPro
465
+ stylesheet, so a theme keeps painting the window exactly as before.
466
+
467
+ The confirm/cancel row built from the `buttons` option is a child of
468
+ `.popup_internal`, **next to** the body and not inside it: React owns the body
469
+ container and empties it when it commits, so a row kept inside the body was
470
+ wiped by the first render. Target it with `.buttons` (or `.lp_confirm` /
471
+ `.lp_cancel`), never with `.popup_body .buttons`. The built-in
472
+ `alert` / `confirm` / `prompt` / `message` modals carry their own row inside
473
+ their body markup.
474
+
475
+ ### Overriding the CSS
476
+
477
+ LayerPro never writes a plain inline style: every declaration lives in the
478
+ stylesheet and is driven by a **class** (the state) or by a **CSS custom
479
+ property** (the value). That means any part of a window can be restyled with
480
+ a plain CSS rule - nothing needs `!important` to win over an inline style.
481
+
482
+ There are three ways to restyle LayerPro, from the most recommended to the most
483
+ surgical.
484
+
485
+ **1. CSS custom properties (recommended).** Every visual detail is driven by a
486
+ variable on `:root`, so theming needs no selectors and no `!important`:
487
+
488
+ ```css
489
+ :root {
490
+ --bar-background: #1f2937;
491
+ --bar-selected-background: #2563eb;
492
+ --bar-color: #f9fafb;
493
+ --window-background: #111827;
494
+ --window-border-radius: 4px;
495
+ --internal-background: #0b1220;
496
+ --body-color: #e5e7eb;
497
+ --button-background-color: #2563eb;
498
+ --button-color: #ffffff;
499
+ }
500
+ ```
501
+
502
+ Variables can also be scoped per popup, because every window lives inside
503
+ `#popup`:
504
+
505
+ ```css
506
+ #popup .popup_window_myDarkPopup {
507
+ --window-background: #000;
508
+ --bar-background: #000;
509
+ }
510
+ ```
511
+
512
+ **2. Target the generated classes.** Each element carries a stable class alias
513
+ next to its id-suffixed class:
514
+
515
+ ```css
516
+ #popup .popup_window { border-radius: 0; }
517
+ #popup .buttons button { background: #2563eb; }
518
+ #popup .popup_option .close:hover { background: #dc2626; }
519
+ #popup_dock { background: #111827; }
520
+ ```
521
+
522
+ The visual **states** are classes too, so they can be restyled like any other
523
+ element:
524
+
525
+ | Class | Applied when |
526
+ |-------|--------------|
527
+ | `.popup_maximized` | the window is maximized |
528
+ | `.popup_iconized` | the window is minimized to the dock |
529
+ | `.popup_dimmed` | another window has the focus |
530
+ | `.popup_content_hidden` | another window has the focus |
531
+ | `.popup_content_visible` | the window has the focus |
532
+ | `.lp_hidden` | the element is hidden |
533
+ | `.lp_fade_in` / `.lp_fade_out` | during a fade |
534
+ | `.lp_no_pointer` | while dragging/resizing |
535
+ | `.lp_no_touch` | on the drag/resize handles |
536
+ | `.lp_no_transition` | while resizing |
537
+
538
+ ```css
539
+ #popup .popup_maximized { inset: 0; bottom: 0; }
540
+ #popup .popup_dimmed { opacity: .8; }
541
+ ```
542
+
543
+ The geometry of a window is carried by custom properties, so it can be
544
+ overridden as well: `--lp-top`, `--lp-left`, `--lp-right`, `--lp-bottom`,
545
+ `--lp-width`, `--lp-height`, `--lp-min-width`, `--lp-min-height`,
546
+ `--lp-max-width`, `--lp-max-height`, `--lp-z-index`.
547
+
548
+ ```css
549
+ #popup .popup_overflow { --lp-left: 50%; --lp-top: 100px; }
550
+ ```
551
+
552
+ **3. Cascade layers (full control).** All LayerPro styles live inside the CSS
553
+ cascade layer `layerpro`. Unlayered host rules always win, so a host stylesheet
554
+ can restyle the popups without `!important`:
555
+
556
+ ```css
557
+ #popup { border-radius: 4px; }
558
+ ```
559
+
560
+ If the host declares its own layer, it must be declared **before** `layerpro`,
561
+ otherwise it loses:
562
+
563
+ ```css
564
+ @layer app, layerpro;
565
+ ```
566
+
567
+ > Do **not** rely on `!important` to override LayerPro: LayerPro's `!important`
568
+ > rules live inside a layer and are stronger than unlayered host `!important`
569
+ > rules. Use plain rules or a host layer declared before `layerpro`.
570
+
350
571
  ---
351
572
 
352
573
  ## Notes & Migration
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "layerpro",
3
3
  "codeName": "layerpro",
4
- "version": "4.0.0",
4
+ "version": "4.1.0",
5
5
  "description": "The real custom modals, alert, confirm, prompt... for your browser",
6
6
  "main": "./index.cjs",
7
7
  "module": "./index.js",
@@ -18,6 +18,49 @@ interface LayerButtonsConfig {
18
18
  cancel?: LayerButtonConfig
19
19
  }
20
20
 
21
+ /**
22
+ * The window and the document a layer is drawn in.
23
+ * A layer always belongs to one scope: the document of the window it was
24
+ * opened in.
25
+ */
26
+ interface LayerScope {
27
+ win: Window
28
+ doc: Document
29
+ }
30
+
31
+ /**
32
+ * Everything that can name the window a layer is drawn in:
33
+ * - a popup id -> the native browser window opened with that id
34
+ * - a Window -> that window, when it is reachable (same origin)
35
+ * - a LayerScope -> that scope
36
+ */
37
+ type LayerTarget = string | number | Window | LayerScope
38
+
39
+ /** Options accepted by the modals. */
40
+ interface LayerModalOptions {
41
+ /** The window the modal is drawn in. */
42
+ target?: LayerTarget
43
+ }
44
+
45
+ /** A handle bound to one window, used to open layers in that window. */
46
+ interface LayerWindow {
47
+ /** The target this handle is bound to. */
48
+ readonly id: string | number | Window
49
+ /** The browser window, when it can be reached. */
50
+ readonly win: Window | null
51
+ /** The document of the window, when it can be reached. */
52
+ readonly doc: Document | null
53
+ /** The popup API, bound to that window. */
54
+ readonly popup: {
55
+ open: (props: GenLayerProps) => Promise<GenLayerProps>
56
+ close: (id: string | number | GenLayerProps | null | undefined) => void
57
+ center: (id: string | number | GenLayerProps, offset?: number) => void
58
+ maximize: (props: GenLayerProps) => void
59
+ iconize: (id: string | number | GenLayerProps) => void
60
+ index: (id: string | number | GenLayerProps) => void
61
+ }
62
+ }
63
+
21
64
  interface GenLayerProps {
22
65
  id: string | number
23
66
  body?: React.ReactElement | string | HTMLElement
@@ -61,6 +104,28 @@ interface GenLayerProps {
61
104
  store?: unknown
62
105
  inheritContext?: boolean
63
106
  resilient?: boolean
107
+
108
+ /**
109
+ * Open the layer inside a real browser window (`window.open`) instead of
110
+ * the in-page overlay. The window keeps the layer information: name, icon,
111
+ * dimensions, position, body and buttons.
112
+ */
113
+ native?: boolean
114
+ /**
115
+ * Save the position and the size of the window and restore them on the
116
+ * next open with the same id (stored in `localStorage`).
117
+ */
118
+ persist?: boolean
119
+ /**
120
+ * Close the native window when the page that opened it is closed.
121
+ * A reload/refresh of the opener does NOT close the native window.
122
+ */
123
+ closeWithOpener?: boolean
124
+ /**
125
+ * The window this layer is drawn in. Without a target the layer is drawn
126
+ * in the document of the realm that loaded LayerPro.
127
+ */
128
+ target?: LayerTarget
64
129
  }
65
130
 
66
131
  /**
@@ -78,15 +143,21 @@ interface GenLayerProps {
78
143
  config: (opts: { nonce?: string }) => void
79
144
 
80
145
  /** Open a LayerPro alert. Returns a Promise that resolves when the popup closes. */
81
- readonly alert: (txt: string) => Promise<void>
146
+ readonly alert: (txt: string, opts?: LayerModalOptions) => Promise<void>
82
147
  /** Open a LayerPro prompt. Returns a Promise that resolves to the input string. */
83
- readonly prompt: (txt: string, defaultValue?: string) => Promise<string>
148
+ readonly prompt: (txt: string, defaultValue?: string, opts?: LayerModalOptions) => Promise<string>
84
149
  /** Open a LayerPro message popup. Returns a Promise that resolves when it closes. */
85
- readonly message: (txt: string) => Promise<void>
150
+ readonly message: (txt: string, opts?: LayerModalOptions) => Promise<void>
86
151
  /** Open a LayerPro confirm. Returns a Promise that resolves to a boolean. */
87
- readonly confirm: (txt: string) => Promise<boolean>
152
+ readonly confirm: (txt: string, opts?: LayerModalOptions) => Promise<boolean>
88
153
  readonly credits: (props: { name: string, version: string, description: string, license: string, author: { name: string, email: string } }) => void
89
154
 
155
+ /**
156
+ * A handle bound to one window. Every layer opened through the handle is
157
+ * drawn in that window, not in the one that loaded LayerPro.
158
+ */
159
+ readonly window: (target: string | number | Window) => LayerWindow
160
+
90
161
  setProps: (obj: Record<string, unknown>, options?: { enumerable?: boolean, writable?: boolean, configurable?: boolean }) => void
91
162
 
92
163
  /** Opt-in to override the browser's native `alert`/`confirm`/`prompt`/`message`
@@ -107,10 +178,12 @@ interface GenLayerProps {
107
178
  index: (id: string | number | GenLayerProps) => void
108
179
  close: (id: string | number | GenLayerProps | null | undefined) => void
109
180
  unmount: (id: string) => void
181
+ /** Remove a popup and its DOM synchronously. */
182
+ destroy: (id: string) => void
110
183
  cleanupEvents: (id: string) => void
111
184
  abortSource: (id: string) => void
112
185
  /** @internal - managed internally for stacking order; do not call. */
113
- zIndex: (props?: GenLayerProps) => number
186
+ zIndex: (scope?: LayerScope) => number
114
187
  useReactAdapter?: (props: GenLayerProps, container: HTMLElement) => void
115
188
  }
116
189