@rozie-ui/dialog-angular 0.0.0-stage → 0.2.5
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/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +91 -2
- package/dist/Dialog.d.ts +97 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +92 -0
- package/dist/fesm2022/rozie-ui-dialog-angular.mjs +434 -0
- package/dist/fesm2022/rozie-ui-dialog-angular.mjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/internal/scrollLock.d.ts +4 -0
- package/dist/internal/scrollLock.d.ts.map +1 -0
- package/dist/rozie-ui-dialog-angular.d.ts.map +1 -0
- package/package.json +82 -3
- package/src/Dialog.ts +414 -0
- package/src/index.ts +1 -0
- package/src/internal/scrollLock.ts +48 -0
- package/src/themes/base.css +58 -0
- package/src/themes/bootstrap.css +37 -0
- package/src/themes/material.css +37 -0
- package/src/themes/shadcn.css +38 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @rozie-ui/dialog-angular
|
|
2
|
+
|
|
3
|
+
## 0.2.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e9e5f05: README fixes: the Angular example now uses the component's real `<rozie-dialog>` selector (the `<Dialog>` tag it showed fails to compile), and the Svelte theming import is shown as a TypeScript snippet.
|
|
8
|
+
|
|
9
|
+
## 0.2.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 05cb9db: Regenerated with the compiler fixes found while building `@rozie-ui/select`. Depending on the target and the component:
|
|
14
|
+
- Angular: a two-way (`model: true`) prop is now controlled while the consumer binds a value — a write by the component only emits `<name>Change`, so a consumer that refuses the change keeps the control on its value. The prop keeps its `ModelSignal` type. `$onUpdate` hooks now run (after every render); they never ran before. An output named like a native bubbling event (`change`, `submit`, `reset`, `select`) no longer also delivers the native event to a consumer's binding.
|
|
15
|
+
- Vue: a two-way prop bound one-way (`:value` without `v-model`) is now controlled too: the component's write emits `update:value` and the control keeps the consumer's value.
|
|
16
|
+
- React: an `$expose` verb now runs in `flushSync`, so calls made in one tick each see what the call before left; primitive script constants are plain `const`s; a body statement no longer reads a binding declared after it on the first render.
|
|
17
|
+
- Svelte, Solid: a `$watch` now sees a `$data` write made in `$onMount`.
|
|
18
|
+
- Lit: a `$watch` whose getter reads both props and `$data` re-runs when only the prop changes; duplicated comments in lifecycle bodies are gone.
|
|
19
|
+
|
|
20
|
+
- 05cb9db: First release of `@rozie-ui/dialog` on React, Vue, Svelte, Angular and Lit. The Solid leaf has been on npm since the 2026-09-26 wave; all six targets of the family now ship at the same version, so one version number means one component revision everywhere.
|
|
21
|
+
|
|
22
|
+
The headless, accessible modal dialog is built on the native `<dialog>` element and `showModal()`: top-layer rendering without a portal, a native `::backdrop` scrim, a real focus trap, Escape-to-dismiss, focus restoration, and a ref-counted `<html>` scroll lock that survives nested dialogs. It has a two-way `open` binding, a typed `close` event (`{ reason: 'backdrop' | 'escape' | 'programmatic' }`), an `initialFocus` prop, `show()` / `hide()` handle verbs, and design-system token bridges for shadcn/ui, Material 3 and Bootstrap 5.
|
|
23
|
+
|
|
24
|
+
The Vue leaf ships a `web-types.json` and the Lit leaf a `custom-elements.json`, so JetBrains IDEs and `lit-plugin` complete the props, events and slots.
|
|
25
|
+
|
|
26
|
+
- 05cb9db: Dialog fixes found by the pre-release audit (all six targets):
|
|
27
|
+
- A press that started inside the panel (selecting text, a drag) and was released over the scrim closed the dialog with reason `'backdrop'`. It no longer does; only a press that starts on the scrim is a backdrop click.
|
|
28
|
+
- A native close that did not go through the component (a `<form method="dialog">` submit inside the panel, or a stray `dialog.close()`) left `open` set to `true` over a closed dialog, which then could not be reopened. The `open` model now follows, and `close` fires with `reason: 'programmatic'`.
|
|
29
|
+
- `hide()` on a dialog that is not open no longer writes the model or emits a `close` for a dismissal that never happened.
|
|
30
|
+
- Lit: `ariaLabelledby` could not reach a heading in the slotted content (an `aria-labelledby` id does not cross a shadow root), so the dialog had no accessible name. It is now bridged with `ariaLabelledByElements` where the browser supports it (Chrome and Edge 135+, Safari 16.4+); elsewhere use `ariaLabel`.
|
|
31
|
+
- The handle's `show()` and `hide()` are typed `() => void` on every target (React, Solid and Lit typed them `(...args: any[]) => any`). A call with an argument is now a type error.
|
|
32
|
+
- `ariaLabel` and `ariaLabelledby` are dropped from the `<dialog>` when they are `null`, so a strict consumer compiling the Vue or React source typechecks.
|
|
33
|
+
|
|
34
|
+
- 05cb9db: A `Dialog` removed while open now releases its scroll lock and returns focus; a dialog that mounts closed no longer releases another open dialog's lock; new `initialFocus` prop (a selector or Element focused when the dialog opens).
|
|
35
|
+
- 05cb9db: Dialog: the `close` event handler now has a real payload type instead of `(...args: any[]) => void`. It receives `DialogClosePayload` (`{ reason: DialogCloseReason }`, where the reason is `'backdrop' | 'escape' | 'programmatic'`). Both types are exported from the package entry. A handler written against a different payload shape may now be rejected by the type checker.
|
|
36
|
+
- Updated dependencies [05cb9db]
|
|
37
|
+
- @rozie/runtime-angular@0.10.1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 One Learning Community LTD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rozie-ui/dialog-angular
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Idiomatic **angular** `Dialog` — a headless, fully-accessible **modal dialog** built on the browser's native `<dialog>` element + `showModal()`, compiled from one [Rozie](https://github.com/One-Learning-Community/rozie.js) source. The platform IS the engine: top-layer rendering that escapes `z-index` / `overflow` / `transform` ancestors with **no portal/teleport**, a native `::backdrop` scrim, a real focus trap, Esc-to-dismiss, and focus restoration on close — all for free. Rozie owns the author-side API: the two-way `open` binding, the open↔native reconcile, backdrop/escape close policy, optional scroll-lock, and a fully-tokenised skin. This package is generated; do not edit `src/` by hand.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @rozie-ui/dialog-angular
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `@angular/core + @angular/common + @angular/forms`. Install them alongside this package.
|
|
12
|
+
|
|
13
|
+
Also installed: `@rozie/runtime-angular` — Rozie's small, tree-shaken runtime helper package (controllable state, keyboard navigation, event modifiers, and safe interpolation). It arrives as a regular dependency, so npm pulls it for you. Your bundler keeps only the helpers this component actually uses — typically a few hundred bytes to a few KB, minified and gzipped. [What's in it and what it costs](https://github.com/One-Learning-Community/rozie.js/blob/main/docs/guide/output-and-runtime.md).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Component } from '@angular/core';
|
|
19
|
+
import { Dialog } from '@rozie-ui/dialog-angular';
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-demo',
|
|
23
|
+
standalone: true,
|
|
24
|
+
imports: [Dialog],
|
|
25
|
+
template: `
|
|
26
|
+
<button (click)="open = true">Open dialog</button>
|
|
27
|
+
|
|
28
|
+
<rozie-dialog [(open)]="open" ariaLabelledby="confirm-title" (close)="onClose($event)">
|
|
29
|
+
<h2 id="confirm-title">Delete file?</h2>
|
|
30
|
+
<p>This cannot be undone.</p>
|
|
31
|
+
<button (click)="open = false">Cancel</button>
|
|
32
|
+
<button (click)="open = false">Delete</button>
|
|
33
|
+
</rozie-dialog>
|
|
34
|
+
`,
|
|
35
|
+
})
|
|
36
|
+
export class DemoComponent {
|
|
37
|
+
open = false;
|
|
38
|
+
onClose(e: { reason: 'backdrop' | 'escape' | 'programmatic' }) {
|
|
39
|
+
console.log('closed:', e.reason);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Theming
|
|
45
|
+
|
|
46
|
+
Every cosmetic value is a `--rozie-dialog-*` CSS custom property — override any of them at any ancestor scope. The structural behaviour (top-layer, `::backdrop`, focus trap, centering) comes from the native `<dialog>` and is not tokenised. Ready-made design-system bridges ship in the package:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import '@rozie-ui/dialog-angular/themes/shadcn.css'; // or material.css, bootstrap.css, base.css
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Props
|
|
53
|
+
|
|
54
|
+
| Name | Type | Default | Two-way (model) | Required |
|
|
55
|
+
| --- | --- | --- | :---: | :---: |
|
|
56
|
+
| `open` | `Boolean` | `false` | ✓ | |
|
|
57
|
+
| `disableBackdropClose` | `Boolean` | `false` | | |
|
|
58
|
+
| `disableEscapeClose` | `Boolean` | `false` | | |
|
|
59
|
+
| `disableScrollLock` | `Boolean` | `false` | | |
|
|
60
|
+
| `initialFocus` | `String \| Element` | `null` | | |
|
|
61
|
+
| `ariaLabel` | `String` | `null` | | |
|
|
62
|
+
| `ariaLabelledby` | `String` | `null` | | |
|
|
63
|
+
|
|
64
|
+
## Events
|
|
65
|
+
|
|
66
|
+
| Event | Description |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `close` | Fired whenever the dialog dismisses — through a backdrop click, the Escape key, a programmatic `hide()`, or a native close that did not go through the component (such as a `<form method="dialog">` submit). Payload `{ reason }` where `reason` is `'backdrop'`, `'escape'`, or `'programmatic'` (the last covers `hide()` and any native close). The two-way `open` model is set to `false` on the same tick, so you usually only need this to learn *why* it closed (e.g. to skip a confirmation on an explicit Cancel). |
|
|
69
|
+
|
|
70
|
+
## Imperative handle
|
|
71
|
+
|
|
72
|
+
Beyond props, the component exposes imperative methods (declared once in the Rozie source via `$expose`). Grab a handle with the native ref mechanism and call them directly. The verbs are `show()` / `hide()` (not `open`/`close`) — `open` is the model prop and `close` is the dismiss event, so these names sidestep both collisions and are not inherited `HTMLElement` members:
|
|
73
|
+
|
|
74
|
+
| Method | Description |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `show` | Open the dialog imperatively (sets the two-way `open` model to `true`). Equivalent to a consumer write of `open = true`; the native `<dialog>.showModal()` runs on the next reconcile. |
|
|
77
|
+
| `hide` | Close the dialog imperatively. Sets `open` to `false` and emits `close` with `{ reason: 'programmatic' }`. A no-op when the dialog is not open (nothing is dismissed, so no `close` is emitted). |
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
@Component({ /* ... */ })
|
|
81
|
+
export class DemoComponent {
|
|
82
|
+
@ViewChild(Dialog) dialog!: Dialog; // or the viewChild() signal
|
|
83
|
+
openIt() { this.dialog.show(); }
|
|
84
|
+
closeIt() { this.dialog.hide(); }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Slots
|
|
89
|
+
|
|
90
|
+
| Slot | Params |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| (default) | |
|
package/dist/Dialog.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ElementRef, TemplateRef } from '@angular/core';
|
|
2
|
+
import { RozieSlot } from '@rozie/runtime-angular';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/** Why the dialog closed: a scrim click, the Escape key, or a programmatic `hide()`. */
|
|
5
|
+
export type DialogCloseReason = 'backdrop' | 'escape' | 'programmatic';
|
|
6
|
+
/** The `close` event payload. */
|
|
7
|
+
export interface DialogClosePayload {
|
|
8
|
+
reason: DialogCloseReason;
|
|
9
|
+
}
|
|
10
|
+
interface DefaultCtx {
|
|
11
|
+
}
|
|
12
|
+
export declare class Dialog {
|
|
13
|
+
/**
|
|
14
|
+
* Whether the dialog is shown (two-way `r-model`). The sole `model: true` prop — two-way bind it (`r-model:open` / `v-model:open` / `bind:open` / `[(open)]`) and Dialog reconciles the native `<dialog>` to it via `showModal()` / `close()`. Every close path (backdrop, Escape, programmatic `hide()`) writes `open = false` and emits `close`.
|
|
15
|
+
* @example
|
|
16
|
+
* <rozie-dialog [(open)]="confirmOpen" ariaLabelledby="confirm-title" />
|
|
17
|
+
*/
|
|
18
|
+
readonly __rozieModelIn_open: import("@angular/core").InputSignal<boolean>;
|
|
19
|
+
openChange: import("@angular/core").OutputEmitterRef<boolean>;
|
|
20
|
+
open: import("@angular/core").ModelSignal<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Opt **out** of backdrop-click-to-dismiss. By default a click on the scrim (the `<dialog>` element itself, outside the content panel) closes the dialog with `reason: 'backdrop'`; set this to require an explicit action.
|
|
23
|
+
*/
|
|
24
|
+
disableBackdropClose: import("@angular/core").InputSignal<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Opt **out** of Escape-to-dismiss. By default the native `cancel` event (Esc) closes with `reason: 'escape'`; the component `preventDefault()`s it so the close always flows through the `open` model. Set this to keep the dialog open on Escape (e.g. a required confirmation).
|
|
27
|
+
*/
|
|
28
|
+
disableEscapeClose: import("@angular/core").InputSignal<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Opt **out** of locking `<html>` scroll while the dialog is open. By default `document.documentElement` `overflow` is set to `hidden` for the duration the dialog is shown; set this to leave background scrolling enabled.
|
|
31
|
+
*/
|
|
32
|
+
disableScrollLock: import("@angular/core").InputSignal<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* What to focus when the dialog opens: a CSS selector matched inside the dialog content, or an Element. By default the native `showModal()` choice applies: the first element with `autofocus`, otherwise the first focusable element. Use it to start on a specific field (e.g. `initialFocus="input[name=title]"`) without waiting for the dialog to mount. A selector that matches nothing, or an element that is not focusable, leaves the native choice in place.
|
|
35
|
+
* @example
|
|
36
|
+
* <rozie-dialog [(open)]="renameOpen" initialFocus="#label-name" />
|
|
37
|
+
*/
|
|
38
|
+
initialFocus: import("@angular/core").InputSignal<string | Element>;
|
|
39
|
+
/**
|
|
40
|
+
* Accessible name for the dialog (`aria-label`) when there is no visible title to point at. Prefer `ariaLabelledby` when a visible heading exists.
|
|
41
|
+
*/
|
|
42
|
+
ariaLabel: import("@angular/core").InputSignal<string>;
|
|
43
|
+
/**
|
|
44
|
+
* The `id` of the element that titles the dialog (`aria-labelledby`) — preferred over `ariaLabel` when a visible heading exists inside the dialog. On the Lit element the dialog lives in a shadow root while the heading is slotted light DOM, so the attribute alone cannot reach it; Dialog hands the browser the element directly (`ariaLabelledByElements`) where that is supported (Chrome/Edge 135+, Safari 16.4+) — elsewhere, use `ariaLabel` on Lit.
|
|
45
|
+
*/
|
|
46
|
+
ariaLabelledby: import("@angular/core").InputSignal<string>;
|
|
47
|
+
panelEl: import("@angular/core").Signal<ElementRef<HTMLDivElement>>;
|
|
48
|
+
close: import("@angular/core").OutputEmitterRef<DialogClosePayload>;
|
|
49
|
+
defaultTpl?: TemplateRef<DefaultCtx>;
|
|
50
|
+
templates: import("@angular/core").InputSignal<Record<string, TemplateRef<unknown>>>;
|
|
51
|
+
__rozieFills: import("@angular/core").Signal<readonly RozieSlot[]>;
|
|
52
|
+
__rozieFillMap: import("@angular/core").Signal<Record<string, TemplateRef<unknown>>>;
|
|
53
|
+
private __rozieWatchInitial_0;
|
|
54
|
+
constructor();
|
|
55
|
+
ngAfterViewInit(): void;
|
|
56
|
+
dialogEl: HTMLDialogElement | null;
|
|
57
|
+
holdsLock: boolean;
|
|
58
|
+
returnFocusTo: HTMLElement | null;
|
|
59
|
+
pressStartedInside: boolean;
|
|
60
|
+
setScrollLock: (lock: any) => void;
|
|
61
|
+
deepActiveElement: () => any;
|
|
62
|
+
focusInitial: (panel: any) => void;
|
|
63
|
+
bridgeLabelledBy: (el: any) => void;
|
|
64
|
+
sync: (isOpen: any) => void;
|
|
65
|
+
closeWith: (reason: any) => void;
|
|
66
|
+
onCancel: (e: any) => void;
|
|
67
|
+
onPointerDown: (e: any) => void;
|
|
68
|
+
onClick: (e: any) => void;
|
|
69
|
+
onNativeClose: () => void;
|
|
70
|
+
show: () => void;
|
|
71
|
+
hide: () => void;
|
|
72
|
+
private __rozieCvaOnChange;
|
|
73
|
+
private __rozieCvaOnTouchedFn;
|
|
74
|
+
protected __rozieCvaDisabled: import("@angular/core").WritableSignal<boolean>;
|
|
75
|
+
writeValue(v: boolean | null): void;
|
|
76
|
+
registerOnChange(fn: (v: boolean) => void): void;
|
|
77
|
+
registerOnTouched(fn: () => void): void;
|
|
78
|
+
setDisabledState(isDisabled: boolean): void;
|
|
79
|
+
__rozieCvaOnTouched(): void;
|
|
80
|
+
static ngTemplateContextGuard(_dir: Dialog, _ctx: unknown): _ctx is DefaultCtx;
|
|
81
|
+
private __rozieDestroyRef;
|
|
82
|
+
private rozieSpread_0;
|
|
83
|
+
private __rozieApplyAttrs;
|
|
84
|
+
private __rozieGetHostAttrs;
|
|
85
|
+
private __rozieSpread_0_effect;
|
|
86
|
+
private rozieListenersTarget_1;
|
|
87
|
+
private __rozieListenersRenderer;
|
|
88
|
+
private __rozieListenersDisposers_1;
|
|
89
|
+
private __rozieListenersDestroyRegistered_1;
|
|
90
|
+
private __rozieListenersEffect_1;
|
|
91
|
+
rozieDisplay(v: unknown): string;
|
|
92
|
+
rozieAttr(v: unknown): string | null;
|
|
93
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Dialog, never>;
|
|
94
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<Dialog, "rozie-dialog", never, { "__rozieModelIn_open": { "alias": "open"; "required": false; "isSignal": true; }; "disableBackdropClose": { "alias": "disableBackdropClose"; "required": false; "isSignal": true; }; "disableEscapeClose": { "alias": "disableEscapeClose"; "required": false; "isSignal": true; }; "disableScrollLock": { "alias": "disableScrollLock"; "required": false; "isSignal": true; }; "initialFocus": { "alias": "initialFocus"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "ariaLabelledby"; "required": false; "isSignal": true; }; "templates": { "alias": "templates"; "required": false; "isSignal": true; }; }, { "openChange": "openChange"; "close": "close"; }, ["__rozieFills", "defaultTpl"], never, true, never>;
|
|
95
|
+
}
|
|
96
|
+
export default Dialog;
|
|
97
|
+
//# sourceMappingURL=Dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../src/Dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,UAAU,EAAa,WAAW,EAA4I,MAAM,eAAe,CAAC;AAGlP,OAAO,EAAE,SAAS,EAA4H,MAAM,wBAAwB,CAAC;;AAQ7K,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,cAAc,CAAC;AACvE,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,UAAU,UAAU;CAAG;AAEvB,qBA4Ea,MAAM;IACjB;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,+CAA4D;IACxF,UAAU,oDAAqB;IAC/B,IAAI,+CAAwJ;IAC5J;;OAEG;IACH,oBAAoB,+CAAyB;IAC7C;;OAEG;IACH,kBAAkB,+CAAyB;IAC3C;;OAEG;IACH,iBAAiB,+CAAyB;IAC1C;;;;OAIG;IACH,YAAY,wDAA0C;IACtD;;OAEG;IACH,SAAS,8CAAgC;IACzC;;OAEG;IACH,cAAc,8CAAgC;IAC9C,OAAO,6DAAoD;IAC3D,KAAK,+DAAgC;IACe,UAAU,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACzF,SAAS,4EAAsE;IAC/E,YAAY,uDAAqD;IACjE,cAAc,uEASX;IACH,OAAO,CAAC,qBAAqB,CAAQ;;IA2BrC,eAAe;IAIf,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAE1C,SAAS,UAAS;IAGlB,aAAa,EAAE,WAAW,GAAG,IAAI,CAAQ;IAKzC,kBAAkB,UAAS;IAQ3B,aAAa,SAAU,GAAG,UAKxB;IAIF,iBAAiB,YAIf;IAIF,YAAY,UAAW,GAAG,UAiBxB;IAQF,gBAAgB,OAAQ,GAAG,UAQzB;IAWF,IAAI,WAAY,GAAG,UAmBjB;IAEF,SAAS,WAAY,GAAG,UAKtB;IAIF,QAAQ,MAAO,GAAG,UAIhB;IAIF,aAAa,MAAO,GAAG,UAIrB;IAMF,OAAO,MAAO,GAAG,UAOf;IAQF,aAAa,aAIX;IAMF,IAAI,EAAE,MAAM,IAAI,CAEd;IACF,IAAI,EAAE,MAAM,IAAI,CAGd;IAEF,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,qBAAqB,CAAwB;IACrD,SAAS,CAAC,kBAAkB,kDAAiB;IAE7C,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI;IAGnC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAGhD,iBAAiB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAGvC,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;IAG3C,mBAAmB,IAAI,IAAI;IAI3B,MAAM,CAAC,sBAAsB,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,UAAU;IAIrB,OAAO,CAAC,iBAAiB,CAAsB;IAE/C,OAAO,CAAC,aAAa,CAA0C;IAE/D,OAAO,CAAC,iBAAiB,CAA6C;IAEtE,OAAO,CAAC,mBAAmB,CAAkD;IAE7E,OAAO,CAAC,sBAAsB,CAI3B;IAEH,OAAO,CAAC,sBAAsB,CAAmD;IAEjF,OAAO,CAAC,wBAAwB,CAAqB;IAErD,OAAO,CAAC,2BAA2B,CAAyB;IAE5D,OAAO,CAAC,mCAAmC,CAAS;IAEpD,OAAO,CAAC,wBAAwB,CAoB7B;IAEH,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM;IAEhC,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;yCA1TzB,MAAM;2CAAN,MAAM;CA2TlB;AAED,eAAe,MAAM,CAAC"}
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 One Learning Community LTD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @rozie-ui/dialog-angular
|
|
2
|
+
|
|
3
|
+
Idiomatic **angular** `Dialog` — a headless, fully-accessible **modal dialog** built on the browser's native `<dialog>` element + `showModal()`, compiled from one [Rozie](https://github.com/One-Learning-Community/rozie.js) source. The platform IS the engine: top-layer rendering that escapes `z-index` / `overflow` / `transform` ancestors with **no portal/teleport**, a native `::backdrop` scrim, a real focus trap, Esc-to-dismiss, and focus restoration on close — all for free. Rozie owns the author-side API: the two-way `open` binding, the open↔native reconcile, backdrop/escape close policy, optional scroll-lock, and a fully-tokenised skin. This package is generated; do not edit `src/` by hand.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @rozie-ui/dialog-angular
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `@angular/core + @angular/common + @angular/forms`. Install them alongside this package.
|
|
12
|
+
|
|
13
|
+
Also installed: `@rozie/runtime-angular` — Rozie's small, tree-shaken runtime helper package (controllable state, keyboard navigation, event modifiers, and safe interpolation). It arrives as a regular dependency, so npm pulls it for you. Your bundler keeps only the helpers this component actually uses — typically a few hundred bytes to a few KB, minified and gzipped. [What's in it and what it costs](https://github.com/One-Learning-Community/rozie.js/blob/main/docs/guide/output-and-runtime.md).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Component } from '@angular/core';
|
|
19
|
+
import { Dialog } from '@rozie-ui/dialog-angular';
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-demo',
|
|
23
|
+
standalone: true,
|
|
24
|
+
imports: [Dialog],
|
|
25
|
+
template: `
|
|
26
|
+
<button (click)="open = true">Open dialog</button>
|
|
27
|
+
|
|
28
|
+
<rozie-dialog [(open)]="open" ariaLabelledby="confirm-title" (close)="onClose($event)">
|
|
29
|
+
<h2 id="confirm-title">Delete file?</h2>
|
|
30
|
+
<p>This cannot be undone.</p>
|
|
31
|
+
<button (click)="open = false">Cancel</button>
|
|
32
|
+
<button (click)="open = false">Delete</button>
|
|
33
|
+
</rozie-dialog>
|
|
34
|
+
`,
|
|
35
|
+
})
|
|
36
|
+
export class DemoComponent {
|
|
37
|
+
open = false;
|
|
38
|
+
onClose(e: { reason: 'backdrop' | 'escape' | 'programmatic' }) {
|
|
39
|
+
console.log('closed:', e.reason);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Theming
|
|
45
|
+
|
|
46
|
+
Every cosmetic value is a `--rozie-dialog-*` CSS custom property — override any of them at any ancestor scope. The structural behaviour (top-layer, `::backdrop`, focus trap, centering) comes from the native `<dialog>` and is not tokenised. Ready-made design-system bridges ship in the package:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import '@rozie-ui/dialog-angular/themes/shadcn.css'; // or material.css, bootstrap.css, base.css
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Props
|
|
53
|
+
|
|
54
|
+
| Name | Type | Default | Two-way (model) | Required |
|
|
55
|
+
| --- | --- | --- | :---: | :---: |
|
|
56
|
+
| `open` | `Boolean` | `false` | ✓ | |
|
|
57
|
+
| `disableBackdropClose` | `Boolean` | `false` | | |
|
|
58
|
+
| `disableEscapeClose` | `Boolean` | `false` | | |
|
|
59
|
+
| `disableScrollLock` | `Boolean` | `false` | | |
|
|
60
|
+
| `initialFocus` | `String \| Element` | `null` | | |
|
|
61
|
+
| `ariaLabel` | `String` | `null` | | |
|
|
62
|
+
| `ariaLabelledby` | `String` | `null` | | |
|
|
63
|
+
|
|
64
|
+
## Events
|
|
65
|
+
|
|
66
|
+
| Event | Description |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `close` | Fired whenever the dialog dismisses — through a backdrop click, the Escape key, a programmatic `hide()`, or a native close that did not go through the component (such as a `<form method="dialog">` submit). Payload `{ reason }` where `reason` is `'backdrop'`, `'escape'`, or `'programmatic'` (the last covers `hide()` and any native close). The two-way `open` model is set to `false` on the same tick, so you usually only need this to learn *why* it closed (e.g. to skip a confirmation on an explicit Cancel). |
|
|
69
|
+
|
|
70
|
+
## Imperative handle
|
|
71
|
+
|
|
72
|
+
Beyond props, the component exposes imperative methods (declared once in the Rozie source via `$expose`). Grab a handle with the native ref mechanism and call them directly. The verbs are `show()` / `hide()` (not `open`/`close`) — `open` is the model prop and `close` is the dismiss event, so these names sidestep both collisions and are not inherited `HTMLElement` members:
|
|
73
|
+
|
|
74
|
+
| Method | Description |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `show` | Open the dialog imperatively (sets the two-way `open` model to `true`). Equivalent to a consumer write of `open = true`; the native `<dialog>.showModal()` runs on the next reconcile. |
|
|
77
|
+
| `hide` | Close the dialog imperatively. Sets `open` to `false` and emits `close` with `{ reason: 'programmatic' }`. A no-op when the dialog is not open (nothing is dismissed, so no `close` is emitted). |
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
@Component({ /* ... */ })
|
|
81
|
+
export class DemoComponent {
|
|
82
|
+
@ViewChild(Dialog) dialog!: Dialog; // or the viewChild() signal
|
|
83
|
+
openIt() { this.dialog.show(); }
|
|
84
|
+
closeIt() { this.dialog.hide(); }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Slots
|
|
89
|
+
|
|
90
|
+
| Slot | Params |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| (default) | |
|