@ixfx/components 0.14.3 → 0.14.4
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/bundle/index.d.ts +23 -6
- package/bundle/index.js +52 -52
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/docs-user/index.json +2 -2
- package/docs-user/notifier.md +16 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2571,6 +2571,23 @@ interface PillRendererOptions {
|
|
|
2571
2571
|
queueDisplay?: PillQueueDisplay;
|
|
2572
2572
|
/** Extra margin (px) added to the default spacing on each side, letting pills avoid eg. a fixed header/footer/notch. */
|
|
2573
2573
|
safeArea?: PillSafeArea;
|
|
2574
|
+
/**
|
|
2575
|
+
* Render the toasts in the browser's top layer (via the Popover API), so they
|
|
2576
|
+
* appear above modal `<dialog>` backdrops and other popovers regardless of any
|
|
2577
|
+
* `z-index`. Each time a new toast arrives the container is re-promoted, so a
|
|
2578
|
+
* dialog opened after earlier toasts doesn't keep covering later ones.
|
|
2579
|
+
* Browsers without the Popover API silently fall back to normal stacking.
|
|
2580
|
+
*
|
|
2581
|
+
* Caveat: a modal dialog makes the rest of the page inert, including toasts, so
|
|
2582
|
+
* they show above it but can't be clicked until it closes (timed dismissal is
|
|
2583
|
+
* unaffected). Default `true`. Set `false` to manage stacking yourself with `zIndex`.
|
|
2584
|
+
*/
|
|
2585
|
+
topLayer?: boolean;
|
|
2586
|
+
/**
|
|
2587
|
+
* `z-index` for the toast layer when `topLayer` is `false`. Ignored otherwise.
|
|
2588
|
+
* Leave unset to take no part in z-ordering.
|
|
2589
|
+
*/
|
|
2590
|
+
zIndex?: number;
|
|
2574
2591
|
}
|
|
2575
2592
|
declare function createPillRenderer(options?: PillRendererOptions): NotificationRenderer;
|
|
2576
2593
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -10572,11 +10572,15 @@ function restingTransform(edges, dx, dy) {
|
|
|
10572
10572
|
if (edges.horizontal === `middle`) return `translateX(-50%) translate(${dx}px, ${dy}px)`;
|
|
10573
10573
|
return `translate(${dx}px, ${dy}px)`;
|
|
10574
10574
|
}
|
|
10575
|
+
function supportsPopover(el) {
|
|
10576
|
+
return typeof el.showPopover === `function` && typeof el.hidePopover === `function`;
|
|
10577
|
+
}
|
|
10575
10578
|
function createPillRenderer(options = {}) {
|
|
10576
10579
|
const position = options.position ?? `top-right`;
|
|
10577
10580
|
const queueDisplay = options.queueDisplay ?? `list`;
|
|
10578
10581
|
const safeArea = options.safeArea ?? {};
|
|
10579
10582
|
const edges = edgesOf(position);
|
|
10583
|
+
const useTopLayer = options.topLayer ?? true;
|
|
10580
10584
|
let container = null;
|
|
10581
10585
|
let hooks = null;
|
|
10582
10586
|
const pills = /* @__PURE__ */ new Map();
|
|
@@ -10588,9 +10592,20 @@ function createPillRenderer(options = {}) {
|
|
|
10588
10592
|
container.className = `ixfx-notification-container`;
|
|
10589
10593
|
container.setAttribute(`role`, `region`);
|
|
10590
10594
|
container.setAttribute(`aria-label`, `Notifications`);
|
|
10595
|
+
container.style.cssText = `pointer-events: none;`;
|
|
10596
|
+
if (useTopLayer && supportsPopover(container)) {
|
|
10597
|
+
container.setAttribute(`popover`, `manual`);
|
|
10598
|
+
container.style.cssText += `position: fixed; inset: 0 auto auto 0; width: 0; height: 0; margin: 0; padding: 0; border: 0; overflow: visible; background: transparent; color: inherit;`;
|
|
10599
|
+
} else if (!useTopLayer && options.zIndex !== void 0) container.style.cssText += `position: fixed; inset: 0 auto auto 0; width: 0; height: 0; z-index: ${options.zIndex};`;
|
|
10591
10600
|
document.body.appendChild(container);
|
|
10592
10601
|
return container;
|
|
10593
10602
|
}
|
|
10603
|
+
/** Put the container on top of the top-layer stack (above anything opened since it was last shown). */
|
|
10604
|
+
function promote(cont) {
|
|
10605
|
+
if (!cont.hasAttribute(`popover`)) return;
|
|
10606
|
+
if (cont.matches(`:popover-open`)) cont.hidePopover();
|
|
10607
|
+
cont.showPopover();
|
|
10608
|
+
}
|
|
10594
10609
|
function updateAttributes(el, notification) {
|
|
10595
10610
|
el.setAttribute(`notification-type`, notification.type);
|
|
10596
10611
|
el.setAttribute(`message`, notification.message);
|
|
@@ -10675,6 +10690,7 @@ function createPillRenderer(options = {}) {
|
|
|
10675
10690
|
}
|
|
10676
10691
|
function setNotifications(notifications, _queueSize) {
|
|
10677
10692
|
const cont = ensureContainer();
|
|
10693
|
+
let added = false;
|
|
10678
10694
|
const idsToKeep = new Set(notifications.map((n) => n.id));
|
|
10679
10695
|
for (const notification of notifications) {
|
|
10680
10696
|
const existing = pills.get(notification.id);
|
|
@@ -10689,8 +10705,10 @@ function createPillRenderer(options = {}) {
|
|
|
10689
10705
|
notification
|
|
10690
10706
|
});
|
|
10691
10707
|
cont.appendChild(el);
|
|
10708
|
+
added = true;
|
|
10692
10709
|
measureRowHeight(el);
|
|
10693
10710
|
}
|
|
10711
|
+
if (added) promote(cont);
|
|
10694
10712
|
for (const [id] of pills) if (!idsToKeep.has(id)) dismiss(id);
|
|
10695
10713
|
requestAnimationFrame(() => applyLayout(notifications));
|
|
10696
10714
|
}
|
package/docs-user/index.json
CHANGED
package/docs-user/notifier.md
CHANGED
|
@@ -97,6 +97,22 @@ NotificationManager.configure({
|
|
|
97
97
|
| `position` | `'top-left' \| 'top-middle' \| 'top-right' \| 'bottom-left' \| 'bottom-middle' \| 'bottom-right'` | `'top-right'` | Which corner/edge pills anchor to, and the direction they slide in/out from. |
|
|
98
98
|
| `queueDisplay` | `'list' \| 'stack'` | `'list'` | How queued (non-front) messages are shown — see below. |
|
|
99
99
|
| `safeArea` | `{ top?, right?, bottom?, left? }` (px) | `{}` | Extra margin added on top of the default spacing, e.g. to avoid a fixed header/footer/notch. |
|
|
100
|
+
| `topLayer` | `boolean` | `true` | Show toasts in the browser's top layer so they sit above modal dialogs and popovers. See [Stacking with dialogs](#stacking-with-dialogs-and-popovers). |
|
|
101
|
+
| `zIndex` | `number` | — | Only used when `topLayer` is `false`: the `z-index` of the toast layer, for apps that manage stacking themselves. |
|
|
102
|
+
|
|
103
|
+
### Stacking with dialogs and popovers
|
|
104
|
+
|
|
105
|
+
A modal `<dialog>` (opened with `showModal()`) and its backdrop live in the browser's *top layer*, which no `z-index` can beat. So by default the pill renderer puts its container in the top layer too, using the [Popover API](https://developer.mozilla.org/docs/Web/API/Popover_API) (`popover="manual"`), and toasts appear above the backdrop.
|
|
106
|
+
|
|
107
|
+
- **Order follows time.** Top-layer elements stack in the order they were shown. Each time a *new* toast arrives the container is re-shown, so it ends up above a dialog opened after earlier toasts. An already-visible toast is not re-promoted if a dialog opens later; the next toast will be.
|
|
108
|
+
- **Modal dialogs make the page inert.** While a modal dialog is open, everything outside it, including toasts in the top layer, cannot be clicked or focused. Toasts are visible and still time out, but tap-to-dismiss only works once the dialog closes. If toasts must be clickable over a modal, open that dialog non-modally (`show()`), or render toasts inside the dialog.
|
|
109
|
+
- **Manual control.** Pass `topLayer: false` to opt out. Toasts then stack normally; give them a `zIndex` above your own layers if needed:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
createPillRenderer({ topLayer: false, zIndex: 1000 });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- **Older browsers** without the Popover API fall back to normal stacking automatically.
|
|
100
116
|
|
|
101
117
|
### `queueDisplay: 'list'`
|
|
102
118
|
|