@porsche-design-system/components-react 4.3.0 → 4.4.0-rc.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/CHANGELOG.md +21 -0
- package/cjs/lib/components/popover.wrapper.cjs +4 -3
- package/esm/lib/components/popover.wrapper.d.ts +26 -2
- package/esm/lib/components/popover.wrapper.mjs +5 -4
- package/package.json +2 -2
- package/ssr/cjs/components/dist/styles/esm/styles-entry.cjs +200 -84
- package/ssr/cjs/components/dist/utils/esm/utils-entry.cjs +1 -1
- package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/components/popover.wrapper.cjs +5 -4
- package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/popover.cjs +61 -16
- package/ssr/esm/components/dist/styles/esm/styles-entry.mjs +200 -84
- package/ssr/esm/components/dist/utils/esm/utils-entry.mjs +1 -1
- package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/popover.wrapper.mjs +6 -5
- package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/popover.mjs +62 -17
- package/ssr/esm/lib/components/popover.wrapper.d.ts +26 -2
- package/ssr/esm/lib/dsr-components/popover.d.ts +15 -7
package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/popover.wrapper.mjs
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef, useRef } from 'react';
|
|
4
|
-
import { usePrefix, useBrowserLayoutEffect, useMergedClass } from '../../hooks.mjs';
|
|
4
|
+
import { useEventCallback, usePrefix, useBrowserLayoutEffect, useMergedClass } from '../../hooks.mjs';
|
|
5
5
|
import { syncRef } from '../../utils.mjs';
|
|
6
6
|
import { DSRPopover } from '../dsr-components/popover.mjs';
|
|
7
7
|
|
|
8
|
-
const PPopover = /*#__PURE__*/ forwardRef(({ aria, description, direction = 'bottom', className, children, ...rest }, ref) => {
|
|
8
|
+
const PPopover = /*#__PURE__*/ forwardRef(({ aria, compact, description, direction = 'bottom', onDismiss, open, className, children, ...rest }, ref) => {
|
|
9
9
|
const elementRef = useRef(undefined);
|
|
10
|
+
useEventCallback(elementRef, 'dismiss', onDismiss);
|
|
10
11
|
const WebComponentTag = usePrefix('p-popover');
|
|
11
|
-
const propsToSync = [aria, description, direction];
|
|
12
|
+
const propsToSync = [aria, compact, description, direction, open];
|
|
12
13
|
useBrowserLayoutEffect(() => {
|
|
13
14
|
const { current } = elementRef;
|
|
14
|
-
['aria', 'description', 'direction'].forEach((propName, i) => (current[propName] = propsToSync[i]));
|
|
15
|
+
['aria', 'compact', 'description', 'direction', 'open'].forEach((propName, i) => (current[propName] = propsToSync[i]));
|
|
15
16
|
}, propsToSync);
|
|
16
17
|
const props = {
|
|
17
18
|
...rest,
|
|
18
19
|
// @ts-ignore
|
|
19
20
|
...(!process.browser
|
|
20
21
|
? {
|
|
21
|
-
children: (jsx(DSRPopover, { aria, description, direction, children })),
|
|
22
|
+
children: (jsx(DSRPopover, { aria, compact, description, direction, open, children })),
|
|
22
23
|
}
|
|
23
24
|
: {
|
|
24
25
|
children,
|
package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/popover.mjs
CHANGED
|
@@ -1,35 +1,80 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Component } from 'react';
|
|
3
|
-
import '../../provider.mjs';
|
|
4
2
|
import { splitChildren } from '../../splitChildren.mjs';
|
|
3
|
+
import { Component } from 'react';
|
|
5
4
|
import { minifyCss } from '../../minifyCss.mjs';
|
|
6
5
|
import { getPopoverCss as getComponentCss$v } from '../../../../../../components/dist/styles/esm/styles-entry.mjs';
|
|
7
|
-
import {
|
|
8
|
-
import { PIcon } from '../components/icon.wrapper.mjs';
|
|
6
|
+
import { createTopLayerController, parseAndGetAriaAttributes } from '../../../../../../components/dist/utils/esm/utils-entry.mjs';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
* @slot {"name": "", "description": "Default slot for the popover content." }
|
|
9
|
+
* @slot {"name": "", "description": "Default slot for the popover content. Ignored when the `description` prop is set, which takes precedence." }
|
|
12
10
|
* @slot {"name": "button", "description": "Renders a custom trigger button. When used, the default info button is replaced." }
|
|
11
|
+
*
|
|
12
|
+
* @controlled {"props": ["open"], "event": "dismiss"}
|
|
13
13
|
*/
|
|
14
|
+
// The panel is a native `[popover="manual"]` element that the component promotes to the `#top-layer` itself, so it
|
|
15
|
+
// always renders above surrounding content regardless of ancestor stacking contexts. The component supports two modes:
|
|
16
|
+
// - uncontrolled: `open` is omitted and the component owns visibility via the internal `isOpen` state (toggled by the
|
|
17
|
+
// default info button or a slotted trigger); dismissal closes it directly.
|
|
18
|
+
// - controlled: `open` is a boolean and the consumer owns visibility via a slotted `button`; dismissal only emits
|
|
19
|
+
// `dismiss` and the consumer flips `open`. See `isControlled` / `effectiveOpen` for how the two are reconciled.
|
|
14
20
|
class DSRPopover extends Component {
|
|
15
21
|
host;
|
|
16
22
|
isOpen = false;
|
|
17
|
-
popover
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
// The `[popover]` panel element on the #top-layer that holds the content and the arrow.
|
|
24
|
+
refPopover;
|
|
25
|
+
// The default info button rendered in the Shadow DOM (only present when no `button` slot is used).
|
|
26
|
+
refButton;
|
|
27
|
+
// The `<slot name="button">` element (only present when a custom trigger is projected); its assigned element is the
|
|
28
|
+
// actual trigger, see `triggerElement`.
|
|
29
|
+
refSlotButton;
|
|
30
|
+
// The visual arrow pointing from the panel to the trigger; positioned by Floating UI's `arrow` middleware.
|
|
31
|
+
refArrow;
|
|
32
|
+
// Teardown for the active Floating UI `autoUpdate` subscription; `undefined` while not positioning.
|
|
21
33
|
cleanUpAutoUpdate;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
// The trigger element `autoUpdate` is currently anchored to, so it can be rebound when the trigger identity changes.
|
|
35
|
+
boundTriggerElement;
|
|
36
|
+
// Tracks whether the document-level dismiss listeners (outside click / Escape / pointer) are currently registered.
|
|
37
|
+
hasDismissListeners = false;
|
|
38
|
+
// Tracks whether a pointer button is currently pressed. Lets `onFocusout` defer pointer-driven focus loss (a click on
|
|
39
|
+
// an outside element, already handled by `onClickOutside`) to that handler, so a single outside pointer interaction
|
|
40
|
+
// emits `dismiss` once instead of twice. `onFocusout` then only dismisses on keyboard focus moves (Tab / Shift+Tab).
|
|
41
|
+
isPointerInteraction = false;
|
|
42
|
+
// Tracks whether the current pointer gesture *started* inside the trigger or panel. A `click` only fires on the
|
|
43
|
+
// nearest common ancestor of `mousedown`/`mouseup`, so pressing inside the panel (e.g. starting a text selection),
|
|
44
|
+
// dragging out and releasing outside retargets the resulting `click` to an ancestor *outside* the popover. Without
|
|
45
|
+
// this flag `onClickOutside` would then wrongly dismiss. Captured at `pointerdown` time (where `composedPath()` is
|
|
46
|
+
// still valid) and consumed on the following `click`, so dismissal only happens when the gesture started outside too.
|
|
47
|
+
isPointerDownInside = false;
|
|
48
|
+
// Keeps the panel on the #top-layer during its fade-out (Chromium via `overlay`; Safari/Firefox via a deferred hide).
|
|
49
|
+
topLayer = createTopLayerController({
|
|
50
|
+
getElement: () => this.props.refPopover,
|
|
51
|
+
isShown: () => !!this.props.refPopover?.matches(':popover-open'),
|
|
52
|
+
show: () => this.props.refPopover?.showPopover(),
|
|
53
|
+
hide: () => this.props.refPopover?.hidePopover(),
|
|
54
|
+
});
|
|
55
|
+
get isControlled() {
|
|
56
|
+
// Controlled mode is opted into purely by passing a boolean `open`; an omitted (`undefined`) prop means the
|
|
57
|
+
// component manages its own visibility.
|
|
58
|
+
return typeof this.props.open === 'boolean';
|
|
59
|
+
}
|
|
60
|
+
get effectiveOpen() {
|
|
61
|
+
// Single source of truth for "is the panel currently open", regardless of mode: the consumer-owned `open` prop in
|
|
62
|
+
// controlled mode, the internal `isOpen` state otherwise. All render/positioning/dismissal logic reads this.
|
|
63
|
+
return this.props.isControlled ? this.props.open : this.props.isOpen;
|
|
64
|
+
}
|
|
65
|
+
get triggerElement() {
|
|
66
|
+
// Resolves the element that actually acts as the trigger: the default info button in the Shadow DOM, or — when a
|
|
67
|
+
// custom trigger is projected through the `button` slot — the assigned light-DOM element itself (not the `<slot>`).
|
|
68
|
+
// Using the assigned element gives Floating UI an accurate anchor rect and lets `:host` use `display: contents`.
|
|
69
|
+
// Kept correct across dynamic slot changes by the `observeChildren` re-render in `connectedCallback`.
|
|
70
|
+
return this.props.refButton ?? ((this.props.refSlotButton)?.assignedElements()[0]);
|
|
71
|
+
}
|
|
25
72
|
render() {
|
|
26
73
|
const { namedSlotChildren} = splitChildren(this.props.children);
|
|
27
74
|
const hasSlottedButton = namedSlotChildren.filter(({ props: { slot } }) => slot === 'button').length > 0;
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
...{ 'aria-expanded': this.props.isOpen },
|
|
32
|
-
}), children: [jsx(PIcon, { className: "icon", name: "information" }), jsx("span", { className: "label", children: "More information" })] })), this.props.isOpen && (jsxs("div", { popover: "auto", children: [jsx("div", { className: "arrow" }), jsx("div", { className: "content", children: this.props.description ? jsx("p", { children: this.props.description }) : jsx("slot", {}) })] }))] })] }), this.props.children] }));
|
|
75
|
+
const id = 'popover';
|
|
76
|
+
const style = minifyCss(getComponentCss$v(this.props.effectiveOpen, this.props.compact).replace(/(:host {[\S\s]+?})[\S\s]+(button {[\S\s]+?})[\S\s]+(.icon {[\S\s]+?})[\S\s]+(.label {[\S\s]+?})[\S\s]+/, '$1\n$2\n$3\n$4'));
|
|
77
|
+
return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxs(Fragment, { children: [hasSlottedButton ? (jsx("slot", { name: "button" })) : (jsx("button", { type: "button", "aria-label": "More information", "aria-details": id, ...parseAndGetAriaAttributes(this.props.aria), "aria-expanded": this.props.effectiveOpen ? 'true' : 'false' })), jsxs("div", { id: id, popover: "manual", inert: !this.props.effectiveOpen, children: [jsx("div", { className: "arrow" }), this.props.description ? jsx("p", { children: this.props.description }) : jsx("slot", {})] })] })] }), this.props.children] }));
|
|
33
78
|
}
|
|
34
79
|
}
|
|
35
80
|
|
|
@@ -6,7 +6,11 @@ export type PPopoverProps = BaseProps & {
|
|
|
6
6
|
*/
|
|
7
7
|
aria?: SelectedAriaAttributes<PopoverAriaAttribute>;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Reduces padding and spacing for a more compact layout, useful in space-constrained interfaces.
|
|
10
|
+
*/
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Sets the text content displayed inside the popover panel when it is open, providing contextual help or information. Takes precedence over the default slot when both are provided.
|
|
10
14
|
*/
|
|
11
15
|
description?: string;
|
|
12
16
|
/**
|
|
@@ -14,6 +18,14 @@ export type PPopoverProps = BaseProps & {
|
|
|
14
18
|
* @default 'bottom'
|
|
15
19
|
*/
|
|
16
20
|
direction?: PopoverDirection;
|
|
21
|
+
/**
|
|
22
|
+
* Emitted in controlled mode when the user requests to close the popover via the Escape key, an outside click, or when keyboard focus leaves the popover (Tab / Shift+Tab).
|
|
23
|
+
*/
|
|
24
|
+
onDismiss?: (event: CustomEvent<void>) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Controls whether the popover is visible. When set (controlled mode), visibility follows this prop and the consumer owns the open state via a slotted `button`. When omitted (uncontrolled mode), the component manages visibility itself.
|
|
27
|
+
*/
|
|
28
|
+
open?: boolean;
|
|
17
29
|
};
|
|
18
30
|
export declare const PPopover: import("react").ForwardRefExoticComponent<Omit<import("react").DOMAttributes<{}>, "onChange" | "onInput" | "onToggle"> & Pick<import("react").HTMLAttributes<{}>, "suppressHydrationWarning" | "autoFocus" | "className" | "dir" | "hidden" | "id" | "inert" | "inputMode" | "lang" | "slot" | "style" | "tabIndex" | "title" | "translate" | "role"> & {
|
|
19
31
|
/**
|
|
@@ -21,7 +33,11 @@ export declare const PPopover: import("react").ForwardRefExoticComponent<Omit<im
|
|
|
21
33
|
*/
|
|
22
34
|
aria?: SelectedAriaAttributes<PopoverAriaAttribute>;
|
|
23
35
|
/**
|
|
24
|
-
*
|
|
36
|
+
* Reduces padding and spacing for a more compact layout, useful in space-constrained interfaces.
|
|
37
|
+
*/
|
|
38
|
+
compact?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Sets the text content displayed inside the popover panel when it is open, providing contextual help or information. Takes precedence over the default slot when both are provided.
|
|
25
41
|
*/
|
|
26
42
|
description?: string;
|
|
27
43
|
/**
|
|
@@ -29,6 +45,14 @@ export declare const PPopover: import("react").ForwardRefExoticComponent<Omit<im
|
|
|
29
45
|
* @default 'bottom'
|
|
30
46
|
*/
|
|
31
47
|
direction?: PopoverDirection;
|
|
48
|
+
/**
|
|
49
|
+
* Emitted in controlled mode when the user requests to close the popover via the Escape key, an outside click, or when keyboard focus leaves the popover (Tab / Shift+Tab).
|
|
50
|
+
*/
|
|
51
|
+
onDismiss?: (event: CustomEvent<void>) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Controls whether the popover is visible. When set (controlled mode), visibility follows this prop and the consumer owns the open state via a slotted `button`. When omitted (uncontrolled mode), the component manages visibility itself.
|
|
54
|
+
*/
|
|
55
|
+
open?: boolean;
|
|
32
56
|
} & {
|
|
33
57
|
children?: import("react").ReactNode | undefined;
|
|
34
58
|
} & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { Component } from 'react';
|
|
2
2
|
import type { JSX } from 'react';
|
|
3
3
|
/**
|
|
4
|
-
* @slot {"name": "", "description": "Default slot for the popover content." }
|
|
4
|
+
* @slot {"name": "", "description": "Default slot for the popover content. Ignored when the `description` prop is set, which takes precedence." }
|
|
5
5
|
* @slot {"name": "button", "description": "Renders a custom trigger button. When used, the default info button is replaced." }
|
|
6
|
+
*
|
|
7
|
+
* @controlled {"props": ["open"], "event": "dismiss"}
|
|
6
8
|
*/
|
|
7
9
|
export declare class DSRPopover extends Component<any> {
|
|
8
10
|
host: HTMLElement;
|
|
9
11
|
private isOpen;
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
private
|
|
12
|
+
private refPopover;
|
|
13
|
+
private refButton;
|
|
14
|
+
private refSlotButton;
|
|
15
|
+
private refArrow;
|
|
14
16
|
private cleanUpAutoUpdate;
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
+
private boundTriggerElement;
|
|
18
|
+
private hasDismissListeners;
|
|
19
|
+
private isPointerInteraction;
|
|
20
|
+
private isPointerDownInside;
|
|
21
|
+
private topLayer;
|
|
22
|
+
private get isControlled();
|
|
23
|
+
private get effectiveOpen();
|
|
24
|
+
private get triggerElement();
|
|
17
25
|
render(): JSX.Element;
|
|
18
26
|
}
|