@porsche-design-system/components-react 4.4.0-rc.0 → 4.4.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +2 -2
  3. package/ssr/cjs/components/dist/styles/esm/styles-entry.cjs +164 -68
  4. package/ssr/cjs/components/dist/utils/esm/utils-entry.cjs +1 -1
  5. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/banner.cjs +15 -5
  6. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/dialog-base.cjs +3 -7
  7. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/drilldown.cjs +3 -0
  8. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/fc-dismiss-button.cjs +16 -0
  9. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/flyout.cjs +14 -1
  10. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/inline-notification.cjs +2 -2
  11. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/modal.cjs +14 -1
  12. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/popover.cjs +5 -1
  13. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/sheet.cjs +14 -1
  14. package/ssr/esm/components/dist/styles/esm/styles-entry.mjs +164 -69
  15. package/ssr/esm/components/dist/utils/esm/utils-entry.mjs +1 -1
  16. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/banner.mjs +15 -5
  17. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/dialog-base.mjs +3 -7
  18. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/drilldown.mjs +3 -0
  19. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/fc-dismiss-button.mjs +14 -0
  20. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/flyout.mjs +14 -1
  21. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/inline-notification.mjs +2 -2
  22. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/modal.mjs +14 -1
  23. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/popover.mjs +5 -1
  24. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/sheet.mjs +14 -1
  25. package/ssr/esm/lib/dsr-components/banner.d.ts +2 -0
  26. package/ssr/esm/lib/dsr-components/dialog-base.d.ts +2 -2
  27. package/ssr/esm/lib/dsr-components/drilldown.d.ts +1 -0
  28. package/ssr/esm/lib/dsr-components/fc-dismiss-button.d.ts +16 -0
  29. package/ssr/esm/lib/dsr-components/flyout.d.ts +1 -0
  30. package/ssr/esm/lib/dsr-components/modal.d.ts +1 -0
  31. package/ssr/esm/lib/dsr-components/popover.d.ts +1 -0
  32. package/ssr/esm/lib/dsr-components/sheet.d.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -14,6 +14,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
14
14
 
15
15
  ## [Unreleased]
16
16
 
17
+ ## [4.4.0] - 2026-07-15
18
+
19
+ ## [4.4.0-rc.1] - 2026-07-10
20
+
21
+ ### Fixed
22
+
23
+ - `Flyout`, `Modal`, `Sheet` ([#4578](https://github.com/porsche-design-system/porsche-design-system/pull/4578)):
24
+ - enable selecting text within dialog element
25
+ - prevent dialog from being dismissed when a text selection started inside the dialog is released on the backdrop
26
+ - `Popover`, `Banner`: prevent entry animation from playing when initially rendered in an open state
27
+ ([#4578](https://github.com/porsche-design-system/porsche-design-system/pull/4578))
28
+
17
29
  ## [4.4.0-rc.0] - 2026-07-06
18
30
 
19
31
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porsche-design-system/components-react",
3
- "version": "4.4.0-rc.0",
3
+ "version": "4.4.0",
4
4
  "description": "Porsche Design System is a component library designed to help developers create the best experience for software or services distributed by Dr. Ing. h.c. F. Porsche AG.",
5
5
  "keywords": [
6
6
  "porsche",
@@ -21,7 +21,7 @@
21
21
  "url": "https://github.com/porsche-design-system/porsche-design-system"
22
22
  },
23
23
  "dependencies": {
24
- "@porsche-design-system/components-js": "4.4.0-rc.0"
24
+ "@porsche-design-system/components-js": "4.4.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "ag-grid-community": ">= 35.0.0 <36.0.0",
@@ -3849,7 +3849,7 @@ const OPTION_LIST_SAFE_ZONE = 6;
3849
3849
 
3850
3850
  const getCDNBaseURL = () => global.PORSCHE_DESIGN_SYSTEM_CDN_URL + "/porsche-design-system";
3851
3851
 
3852
- const prefix = `[Porsche Design System v${"4.4.0-rc.0"}]` // this part isn't covered by unit tests
3852
+ const prefix = `[Porsche Design System v${"4.4.0"}]` // this part isn't covered by unit tests
3853
3853
  ;
3854
3854
  const consoleError = (...messages) => {
3855
3855
  console.error(prefix, ...messages);
@@ -4222,9 +4222,6 @@ const getComponentCss$19 = () => {
4222
4222
  });
4223
4223
  };
4224
4224
 
4225
- const TOAST_Z_INDEX = 999999;
4226
- const BANNER_Z_INDEX = 99;
4227
-
4228
4225
  const colorMap$3 = {
4229
4226
  primary: ref(colorPrimary),
4230
4227
  'contrast-higher': ref(colorContrastHigher),
@@ -4289,6 +4286,86 @@ const weightMap = {
4289
4286
  bold: ref(fontWeightBold),
4290
4287
  };
4291
4288
 
4289
+ // Inlined `close` icon (mirrors packages/assets/projects/icons/src/close.svg) rendered as a CSS mask, so the dismiss
4290
+ // button needs neither a nested `p-button` nor a `p-icon` (which would fetch the SVG from the CDN asynchronously).
4291
+ const iconClose = getInlineSVGBackgroundImage('<path d="m18 6.706-5.294 5.294 5.294 5.294-.706.706-5.294-5.294-5.294 5.294-.706-.706 5.294-5.294-5.294-5.294.706-.706 5.294 5.294 5.294-5.294z"/>');
4292
+ // NOTE: Values copied (and reduced to the fixed `compact` + `hideLabel` case the dismiss button always uses) from
4293
+ // `getVariantColors` in `packages/components/src/styles/link-button-styles.ts` to keep the visual identical to the
4294
+ // previously nested `p-button`. If the shared button styling changes, mirror it here (or extract a shared helper).
4295
+ const getVariantColors$1 = (variant) => {
4296
+ const colors = {
4297
+ primary: {
4298
+ textColor: ref(colorCanvas),
4299
+ backgroundColor: ref(colorPrimary),
4300
+ backgroundColorHover: ref(colorContrastHigh),
4301
+ },
4302
+ secondary: {
4303
+ textColor: ref(colorPrimary),
4304
+ backgroundColor: ref(colorFrostedStrong),
4305
+ backgroundColorHover: ref(colorFrosted),
4306
+ },
4307
+ };
4308
+ return colors[variant];
4309
+ };
4310
+ /**
4311
+ * Visual styles for the shared native `<button class="dismiss">` dismiss button. Returns the button rule (reset,
4312
+ * variant colors, focus ring, High Contrast Mode, hover, inlined close-icon mask and the visually-hidden label). Each
4313
+ * consumer merges this with its own positioning styles for the `dismiss` key (e.g. grid placement in
4314
+ * `notification-base-styles` / `dialog-base-styles`).
4315
+ *
4316
+ * Reduced to the dismiss button's fixed configuration (always `compact` + `hideLabel`): padding `6px`, full border
4317
+ * radius and no gap — the responsive `compact`/`hideLabel` branches from `getLinkButtonStyles` are therefore collapsed.
4318
+ */
4319
+ const getFCDismissButtonStyles = (variant) => {
4320
+ const { textColor, backgroundColor, backgroundColorHover } = getVariantColors$1(variant);
4321
+ return {
4322
+ all: 'unset',
4323
+ boxSizing: 'border-box',
4324
+ display: 'grid',
4325
+ placeItems: 'center',
4326
+ padding: '6px',
4327
+ borderRadius: ref(radiusFull),
4328
+ font: `${ref(fontWeightNormal$1)} ${ref(typescaleSm$1)} / ${ref(leadingNormal$1)} ${ref(fontPorscheNext$1)}`,
4329
+ backgroundColor,
4330
+ color: textColor,
4331
+ cursor: 'pointer',
4332
+ // The frosted backdrop-filter is only meaningful for the translucent `secondary` (frosted) background. For the
4333
+ // opaque `primary` background it is invisible AND — because the dialog consumer additionally applies
4334
+ // `filter: invert(1)` on the same element (see `getDialogDismissButtonJssStyle`) — the blurred backdrop bleeds past
4335
+ // the circular edge and the invert amplifies it into a visible glow/halo. So it is applied for `secondary` only.
4336
+ ...(variant === 'secondary' && {
4337
+ WebkitBackdropFilter: ref(blurFrosted),
4338
+ backdropFilter: ref(blurFrosted),
4339
+ }),
4340
+ transition: `${getTransition('background-color')}, ${getTransition('color')}`,
4341
+ ...forcedColorsMediaQuery({
4342
+ forcedColorAdjust: 'none',
4343
+ background: 'Canvas',
4344
+ boxShadow: 'inset 0 0 0 2px ButtonBorder',
4345
+ color: 'ButtonText',
4346
+ }),
4347
+ '&:focus-visible': getFocusBaseStyles(),
4348
+ ...hoverMediaQuery({
4349
+ '&:hover': {
4350
+ backgroundColor: backgroundColorHover,
4351
+ ...forcedColorsMediaQuery({
4352
+ background: 'Canvas',
4353
+ }),
4354
+ },
4355
+ }),
4356
+ '&::before': {
4357
+ content: '""',
4358
+ width: ref(leadingNormal$1),
4359
+ height: ref(leadingNormal$1),
4360
+ WebkitMask: `${iconClose} center/contain no-repeat`, // necessary for Sogou browser support :-)
4361
+ mask: `${iconClose} center/contain no-repeat`,
4362
+ background: 'currentColor', // follows the button's text color (variant + HCM `ButtonText`)
4363
+ },
4364
+ // visually-hidden accessible name (mirrors `hideLabel` on the former `p-button`)
4365
+ '& span': getHiddenTextJssStyle(),
4366
+ };
4367
+ };
4368
+
4292
4369
  const getFunctionalComponentNotificationBaseStyles = (state, hasAction, hasDismissButton, hasHeadingOrHeadingSlot) => {
4293
4370
  return {
4294
4371
  '@global': {
@@ -4339,6 +4416,8 @@ const getFunctionalComponentNotificationBaseStyles = (state, hasAction, hasDismi
4339
4416
  },
4340
4417
  ...(hasDismissButton && {
4341
4418
  dismiss: {
4419
+ // native dismiss button visual (secondary variant); grid positioning is applied on top
4420
+ ...getFCDismissButtonStyles('secondary'),
4342
4421
  gridArea: `1/4/-1`,
4343
4422
  alignSelf: 'flex-start',
4344
4423
  marginBlock: `calc(-6 * ${ref(spacingStatic2Xs)})`,
@@ -4379,12 +4458,12 @@ const cssVarInsetX = '--p-banner-inset-x';
4379
4458
  const cssVarPositionTop = '--p-banner-position-top'; // deprecated (aliased)
4380
4459
  const cssVarPositionBottom = '--p-banner-position-bottom'; // deprecated (aliased)
4381
4460
  const topBottomFallback = '56px';
4382
- const cssVariableZIndex = '--_p-banner-a';
4383
- const getComponentCss$18 = (isOpen, position, state, hasDismissButton, hasHeadingOrHeadingSlot) => {
4461
+ const cssVarTransform = '--_p-banner-a';
4462
+ const getComponentCss$18 = (isOpen, position, state, hasDismissButton, hasHeadingOrHeadingSlot, skipEntryTransition) => {
4384
4463
  const duration = isOpen ? 'moderate' : 'short';
4385
4464
  const easing = isOpen ? 'in' : 'out';
4386
- const transition = `visibility 0s linear ${ref(cssVariableTransitionDuration, isOpen ? '0s' : motionDurationMap[duration])},${getTransition('transform', duration, easing)}`;
4387
- return getCss({
4465
+ const transition = getTransition('transform', duration, easing);
4466
+ const css = getCss({
4388
4467
  ...mergeDeep({
4389
4468
  '@global': {
4390
4469
  ':host': {
@@ -4393,46 +4472,36 @@ const getComponentCss$18 = (isOpen, position, state, hasDismissButton, hasHeadin
4393
4472
  ...hostHiddenStyles,
4394
4473
  }),
4395
4474
  },
4396
- ...preventFoucOfNestedElementsStyles,
4397
4475
  '[popover]': {
4398
4476
  all: 'unset',
4399
4477
  position: 'fixed',
4400
- zIndex: ref(cssVariableZIndex, BANNER_Z_INDEX), // Fallback for browsers lacking `transition-behavior: allow-discrete` — keeps the banner visible during fade-out after leaving the top layer.
4401
4478
  ...buildResponsiveStyles(position, (v) => ({
4402
4479
  ...(v === 'top' && {
4480
+ [cssVarTransform]: `translate3d(-50%,calc(-100% - ${ref(cssVarTop, ref(cssVarPositionTop, topBottomFallback))}),0)`,
4403
4481
  insetBlock: `${ref(cssVarTop, ref(cssVarPositionTop, topBottomFallback))} auto`,
4404
- ...(!isOpen && {
4405
- transform: `translate3d(-50%,calc(-100% - ${ref(cssVarTop, ref(cssVarPositionTop, topBottomFallback))}),0)`,
4406
- }),
4407
4482
  }),
4408
4483
  ...(v === 'bottom' && {
4484
+ [cssVarTransform]: `translate3d(-50%,calc(${ref(cssVarBottom, ref(cssVarPositionBottom, topBottomFallback))} + 100%),0)`,
4409
4485
  insetBlock: `auto ${ref(cssVarBottom, ref(cssVarPositionBottom, topBottomFallback))}`,
4410
- ...(!isOpen && {
4411
- transform: `translate3d(-50%,calc(${ref(cssVarBottom, ref(cssVarPositionBottom, topBottomFallback))} + 100%),0)`,
4412
- }),
4413
4486
  }),
4414
4487
  })),
4415
4488
  left: '50vw',
4416
4489
  width: `min(calc(100vw - 2 * ${ref(cssVarInsetX, gridExtendedOffsetBase)}),${ref(cssVarMaxWidth, '100ch')})`,
4490
+ transform: isOpen ? 'translate3d(-50%,0,0)' : ref(cssVarTransform),
4491
+ transition,
4492
+ // keep the popover on the #top-layer while the fade-out runs (Chromium only; see `overlayTransitionSupportsQuery`)
4493
+ ...overlayTransitionSupportsQuery({
4494
+ transition: `${transition},${getTransition('overlay', duration, easing)} allow-discrete, ${getTransition('display', duration, easing)} allow-discrete`,
4495
+ }),
4417
4496
  overlay: 'none',
4497
+ display: 'none',
4418
4498
  '&:popover-open': {
4419
4499
  overlay: 'auto',
4500
+ display: 'grid',
4420
4501
  },
4421
4502
  '&::backdrop': {
4422
- display: 'none',
4503
+ display: 'none', // reset ua-style
4423
4504
  },
4424
- visibility: 'hidden', // element shall not be tabbable with keyboard after fade out transition has finished
4425
- pointerEvents: 'none', // element can't be interacted with mouse
4426
- ...(isOpen && {
4427
- visibility: 'inherit',
4428
- pointerEvents: 'inherit',
4429
- transform: 'translate3d(-50%,0,0)',
4430
- }),
4431
- transition,
4432
- // keep the popover on the #top-layer while the fade-out runs (Chromium only; see `overlayTransitionSupportsQuery`)
4433
- ...overlayTransitionSupportsQuery({
4434
- transition: `${transition},${getTransition('overlay', duration, easing)} allow-discrete`,
4435
- }),
4436
4505
  },
4437
4506
  },
4438
4507
  }, {
@@ -4443,6 +4512,17 @@ const getComponentCss$18 = (isOpen, position, state, hasDismissButton, hasHeadin
4443
4512
  },
4444
4513
  }, getFunctionalComponentNotificationBaseStyles(state, false, hasDismissButton, hasHeadingOrHeadingSlot)),
4445
4514
  });
4515
+ // Both the `[popover]` box (transform) and its nested `.notification` (opacity) need a `@starting-style`: while closed
4516
+ // the popover is `display: none`, so on open `.notification` renders fresh with a computed `opacity: 1` and has no
4517
+ // prior value to transition from — without the starting value it snaps to full opacity instead of fading in. The
4518
+ // opacity is split onto `.notification` (not `[popover]`) so the frosted-glass backdrop-filter renders correctly.
4519
+ //
4520
+ // `skipEntryTransition` omits the append on the component's FIRST render, so an initially-open banner (`open=true` on
4521
+ // page load) computes straight to its visible transform/opacity and appears instantly instead of sliding/fading in.
4522
+ // Every later render passes `false`, so a user-triggered open still animates normally.
4523
+ return isOpen && false
4524
+ ? `${css}\n@starting-style {\n [popover] {\n transform: ${ref(cssVarTransform)};\n }\n .notification {\n opacity: 0;\n }\n}`
4525
+ : css;
4446
4526
  };
4447
4527
 
4448
4528
  // Needed for slotted anchor and hidden label, which then enlarges the hidden label to equal host size and indents the text to be visually hidden.
@@ -5826,28 +5906,14 @@ const dialogHostJssStyle = (background) => {
5826
5906
  [cssVarBackgroundColor]: background === 'surface' ? ref(colorSurface) : ref(colorCanvas),
5827
5907
  };
5828
5908
  };
5909
+ // INTENTIONAL DIVERGENCE from `popover-styles.ts` (kept, not aligned — see the mirrored note there):
5910
+ // This dialog stays permanently rendered and collapses its CLOSED state via delayed `visibility: hidden` + `width/height: 0`
5911
+ // (driven by `isVisible`), so `opacity`/`background` fade in BOTH directions with NO `@starting-style`, and an initial
5912
+ // `open=true` computes straight to the visible state (no entry fade on load). `p-popover`/`p-banner` instead use the native
5913
+ // `[popover]` `display: none`/`:popover-open` toggle plus a `@starting-style` entry fade, because those components mimic the
5914
+ // native popover box model. Both share `createTopLayerController` + the Chromium-only `overlay allow-discrete` transition
5915
+ // (`overlayTransitionSupportsQuery`) and the `inert` toggle for tabbability; only the closed-state expression differs.
5829
5916
  const getFunctionalComponentDialogBaseStyles = (isVisible, backdrop = 'blur') => {
5830
- return {
5831
- dialog: {
5832
- ...dialogBackdropResetJssStyle,
5833
- ...getDialogBackdropTransitionJssStyle(isVisible, backdrop),
5834
- },
5835
- };
5836
- };
5837
- const dialogBackdropResetJssStyle = {
5838
- all: 'unset',
5839
- position: 'fixed',
5840
- inset: 0,
5841
- maxWidth: '100dvw',
5842
- maxHeight: '100dvh',
5843
- overflow: 'hidden',
5844
- display: 'block',
5845
- outline: 0, // we always expect a focusable element to be within the dialog
5846
- '&::backdrop': {
5847
- display: 'none', // ua-style (we can't use it atm because it's not animatable in all browsers)
5848
- },
5849
- };
5850
- const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
5851
5917
  const isBackdropBlur = backdrop === 'blur';
5852
5918
  const duration = isVisible ? 'long' : 'moderate';
5853
5919
  const easing = isVisible ? 'in' : 'out';
@@ -5855,14 +5921,21 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
5855
5921
  // as soon as all browsers are supporting `allow-discrete`, visibility transition shouldn't be necessary anymore
5856
5922
  const transition = `visibility 0s linear ${delay}, width 0s linear ${delay}, height 0s linear ${delay}, ${getTransition('background-color', duration, easing)}, ${getTransition('-webkit-backdrop-filter', duration, easing)}, ${getTransition('backdrop-filter', duration, easing)}`;
5857
5923
  return {
5858
- zIndex: 9999999, // fallback for fade out stacking until `overlay` + `allow-discrete` is supported in all browsers. It tries to mimic #top-layer positioning hierarchy.
5924
+ all: 'unset',
5925
+ position: 'fixed',
5926
+ inset: 0,
5927
+ maxWidth: '100dvw',
5928
+ maxHeight: '100dvh',
5929
+ overflow: 'hidden',
5930
+ display: 'block',
5931
+ userSelect: 'text', // allows text selection within dialog element (e.g. for copy & paste)
5932
+ outline: 0, // we always expect a focusable element to be within the dialog
5859
5933
  ...(isVisible
5860
5934
  ? {
5861
5935
  width: '100dvw',
5862
5936
  height: '100dvh',
5863
5937
  visibility: 'inherit',
5864
5938
  pointerEvents: 'auto',
5865
- overlay: 'auto',
5866
5939
  background: ref(colorBackdrop),
5867
5940
  ...(isBackdropBlur && {
5868
5941
  WebkitBackdropFilter: ref(blurFrosted),
@@ -5878,7 +5951,6 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
5878
5951
  height: '0px',
5879
5952
  visibility: 'hidden', // element shall not be tabbable with keyboard after fade out transition has finished
5880
5953
  pointerEvents: 'none', // element can't be interacted with mouse
5881
- overlay: 'none',
5882
5954
  background: 'transparent',
5883
5955
  }),
5884
5956
  transition,
@@ -5886,6 +5958,13 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
5886
5958
  ...overlayTransitionSupportsQuery({
5887
5959
  transition: `${transition}, ${getTransition('overlay', duration, easing)} allow-discrete`,
5888
5960
  }),
5961
+ overlay: 'none',
5962
+ '&:modal': {
5963
+ overlay: 'auto',
5964
+ },
5965
+ '&::backdrop': {
5966
+ display: 'none', // reset ua-style (we can't use it atm because it's not animatable in all browsers)
5967
+ },
5889
5968
  };
5890
5969
  };
5891
5970
  const getScrollerJssStyle = (position) => {
@@ -5965,6 +6044,9 @@ const getDialogTransitionJssStyle = (isVisible, slideIn) => {
5965
6044
  };
5966
6045
  const getDialogDismissButtonJssStyle = () => {
5967
6046
  return {
6047
+ // native dismiss button visual (primary variant); positioning + the `invert` filter (renders it light on the
6048
+ // dialog) are applied on top below
6049
+ ...getFCDismissButtonStyles('primary'),
5968
6050
  gridArea: '1/3',
5969
6051
  zIndex: 5, // controls layering + creates new stacking context (prevents content within to be above other dialog areas)
5970
6052
  position: 'sticky',
@@ -7089,7 +7171,6 @@ const getComponentCss$V = (isOpen, background, backdrop, position, hasHeader, ha
7089
7171
  ...hostHiddenStyles,
7090
7172
  }),
7091
7173
  },
7092
- ...preventFoucOfNestedElementsStyles,
7093
7174
  slot: {
7094
7175
  ...getSlotJssStyle(),
7095
7176
  '&:not([name])': getSlotMainJssStyle(),
@@ -7112,7 +7193,7 @@ const getComponentCss$V = (isOpen, background, backdrop, position, hasHeader, ha
7112
7193
  '&[name=sub-footer]': getSlotSubFooterJssStyle(),
7113
7194
  }),
7114
7195
  },
7115
- ...getFunctionalComponentDialogBaseStyles(isOpen, backdrop),
7196
+ dialog: getFunctionalComponentDialogBaseStyles(isOpen, backdrop),
7116
7197
  },
7117
7198
  scroller: {
7118
7199
  ...getScrollerJssStyle(isPositionStart ? 'start' : 'end'),
@@ -7942,7 +8023,6 @@ const getComponentCss$C = (isOpen, background, backdrop, fullscreen, hasDismissB
7942
8023
  ...hostHiddenStyles,
7943
8024
  }),
7944
8025
  },
7945
- ...preventFoucOfNestedElementsStyles,
7946
8026
  slot: {
7947
8027
  ...getSlotJssStyle(),
7948
8028
  '&:not([name])': getSlotMainJssStyle(),
@@ -7956,7 +8036,7 @@ const getComponentCss$C = (isOpen, background, backdrop, fullscreen, hasDismissB
7956
8036
  '&[name=footer]': getSlotFooterJssStyle(),
7957
8037
  }),
7958
8038
  },
7959
- ...getFunctionalComponentDialogBaseStyles(isOpen, backdrop),
8039
+ dialog: getFunctionalComponentDialogBaseStyles(isOpen, backdrop),
7960
8040
  },
7961
8041
  scroller: getScrollerJssStyle('fullscreen'),
7962
8042
  modal: {
@@ -8392,7 +8472,7 @@ const cssVariableMaxHeight = '--p-popover-max-h';
8392
8472
  */
8393
8473
  const cssVarPaddingInline = '--p-popover-px';
8394
8474
  /**
8395
- * @css-variable {"name": "--p-popover-py", "description": "Vertical padding of the popover. It is recommended to apply an existing Porsche Design System spacing token, e.g. the CSS declaration `--p-popover-py: var(--p-spacing-static-sm)`, the Tailwind CSS arbitrary property `[--p-popover-py:var(--spacing-static-sm)]` or the equivalent SCSS/JS token.", "defaultValue": "12px"}
8475
+ * @css-variable {"name": "--p-popover-py", "description": "Vertical padding of the popover. It is recommended to apply an existing Porsche Design System spacing token, e.g. the CSS declaration `--p-popover-py: var(--p-spacing-static-sm)`, the Tailwind CSS arbitrary property `[--p-popover-py:var(--spacing-static-sm)]` or the equivalent SCSS/JS token.", "defaultValue": "16px"}
8396
8476
  */
8397
8477
  const cssVarPaddingBlock = '--p-popover-py';
8398
8478
  /**
@@ -8404,14 +8484,19 @@ const iconInfo = getInlineSVGBackgroundImage(`<path d="M12.5 10v6h-1v-6zm0-2v1h-
8404
8484
  * Builds the popover's scoped CSS.
8405
8485
  * @param isOpen - The effective open state; drives the fade-in/fade-out direction and the `@starting-style` append.
8406
8486
  * @param isCompact - Reduces padding/spacing and uses smaller radii (mirrors the `compact` prop).
8407
- * @returns The component CSS string, with a trailing `@starting-style` rule appended while opening.
8487
+ * @param skipEntryTransition - Suppresses the `@starting-style` entry fade. Set on the component's first render so an
8488
+ * initially-open popover (`open=true` on page load) appears instantly instead of fading in. Later user-triggered opens
8489
+ * render with this `false`, so they keep the fade-in.
8490
+ * @returns The component CSS string, with a trailing `@starting-style` rule appended while opening (unless skipped).
8408
8491
  */
8409
- const getComponentCss$v = (isOpen, isCompact) => {
8492
+ const getComponentCss$v = (isOpen, isCompact, skipEntryTransition) => {
8410
8493
  // fade-in on open (via `@starting-style` below), fade-out on close. While closing, the panel keeps `display: grid`
8411
8494
  // (Chromium via `overlay` + `display` `allow-discrete`; Safari/Firefox via the deferred `hidePopover()`), so
8412
8495
  // `display: none` only describes the fully-closed terminal state. Tabbability / a11y-tree removal during the fade-out
8413
8496
  // is handled immediately via the `inert` attribute on the panel (see popover.tsx), so no `visibility` toggle is needed.
8414
- const transition = getTransition('opacity', 'short', isOpen ? 'in' : 'out');
8497
+ const duration = 'short';
8498
+ const easing = isOpen ? 'in' : 'out';
8499
+ const transition = getTransition('opacity', duration, easing);
8415
8500
  const css = getCss({
8416
8501
  '@global': {
8417
8502
  ':host': {
@@ -8430,7 +8515,8 @@ const getComponentCss$v = (isOpen, isCompact) => {
8430
8515
  maxWidth: 'inherit',
8431
8516
  maxHeight: 'inherit',
8432
8517
  boxSizing: 'border-box',
8433
- padding: `${ref(cssVarPaddingBlock, isCompact ? ref(spacingStaticXs$1) : `calc(12 * ${ref(spacingStatic2Xs)})`)} ${ref(cssVarPaddingInline, isCompact ? ref(spacingStaticSm$1) : ref(spacingStaticMd))}`,
8518
+ paddingBlock: `${ref(cssVarPaddingBlock, isCompact ? ref(spacingStaticSm$1) : ref(spacingStaticMd))}`,
8519
+ paddingInline: `${ref(cssVarPaddingInline, isCompact ? ref(spacingStaticSm$1) : ref(spacingStaticMd))}`,
8434
8520
  overflow: 'hidden auto',
8435
8521
  overscrollBehaviorY: 'none',
8436
8522
  },
@@ -8493,7 +8579,7 @@ const getComponentCss$v = (isOpen, isCompact) => {
8493
8579
  transition,
8494
8580
  // keep the popover on the #top-layer while the fade-out runs (Chromium only; see `overlayTransitionSupportsQuery`)
8495
8581
  ...overlayTransitionSupportsQuery({
8496
- transition: `${transition},${getTransition('overlay', 'short', isOpen ? 'in' : 'out')} allow-discrete,${getTransition('display', 'short', isOpen ? 'in' : 'out')} allow-discrete`,
8582
+ transition: `${transition},${getTransition('overlay', duration, easing)} allow-discrete, ${getTransition('display', duration, easing)} allow-discrete`,
8497
8583
  }),
8498
8584
  // Unlike `opacity` (driven by the `isOpen` render flag), `overlay` and `display` are toggled via the
8499
8585
  // `:popover-open` UA state instead of `isOpen`. Both are owned by the browser: they only flip once
@@ -8537,7 +8623,17 @@ const getComponentCss$v = (isOpen, isCompact) => {
8537
8623
  // engines (< Chromium 117 / Safari 17.5 / Firefox 129) ignore the unknown at-rule and skip the entry fade (graceful
8538
8624
  // degradation). Formatting mirrors JSS output so the unit-test CSS parser can read it.
8539
8625
  //
8540
- // ALTERNATIVE APPROACH (not implemented) mirror the `dialog-base` (p-modal/p-flyout) pattern and drop `@starting-style`:
8626
+ // `skipEntryTransition` omits the append on the component's FIRST render, so an initially-open popover (`open=true` on
8627
+ // page load) computes straight to `opacity: 1` and appears instantly instead of fading in. Every later render passes
8628
+ // `false`, so a user-triggered open still fades in normally.
8629
+ //
8630
+ // ALTERNATIVE APPROACH (INTENTIONALLY NOT IMPLEMENTED) — mirror the `dialog-base` (p-modal/p-flyout) pattern and drop `@starting-style`:
8631
+ // Decision: KEEP this divergence (see the mirrored note in `common/dialog-base/dialog-base-styles.ts`). `p-popover`/`p-banner`
8632
+ // mimic the native `[popover]` box model, so they use the native `display: none`/`:popover-open` toggle + `@starting-style`
8633
+ // entry fade rather than the dialog's permanently-rendered `visibility`/`width`/`height` collapse. Both approaches share
8634
+ // `createTopLayerController`, the Chromium-only `overlay allow-discrete` transition (`overlayTransitionSupportsQuery`) and
8635
+ // the `inert` toggle for tabbability; only the closed-state expression differs. The alternative below is documented for
8636
+ // context (and to record its one upside — no entry fade on initial `open=true`), but is deliberately left unimplemented.
8541
8637
  // • Keep the panel permanently rendered (never `display: none`); collapse the closed state via `visibility: hidden`
8542
8638
  // + `width/height: 0` instead. Because the element stays rendered, `opacity` fades normally in BOTH directions with
8543
8639
  // no `@starting-style`, and an initial `open=true` computes straight to `opacity: 1` (appears instantly, no entry
@@ -8553,7 +8649,7 @@ const getComponentCss$v = (isOpen, isCompact) => {
8553
8649
  // tabbable during the fade-out).
8554
8650
  // • Trade-off: removes the `@starting-style` raw-CSS append + the initial-load fade, at the cost of reintroducing the
8555
8651
  // delayed `visibility` plus delayed `width/height` and tighter coupling to the motion duration.
8556
- return isOpen ? `${css}\n@starting-style {\n [popover] {\n opacity: 0;\n }\n}` : css;
8652
+ return isOpen && !skipEntryTransition ? `${css}\n@starting-style {\n [popover] {\n opacity: 0;\n }\n}` : css;
8557
8653
  };
8558
8654
 
8559
8655
  const cssVarInternalRadioGroupOptionScaling = '--_p-radio-group-option-a';
@@ -9133,7 +9229,6 @@ const getComponentCss$n = (isOpen, background, hasDismissButton) => {
9133
9229
  ...hostHiddenStyles,
9134
9230
  }),
9135
9231
  },
9136
- ...preventFoucOfNestedElementsStyles,
9137
9232
  slot: {
9138
9233
  ...getSlotJssStyle(),
9139
9234
  '&:not([name])': getSlotMainJssStyle(),
@@ -9142,7 +9237,7 @@ const getComponentCss$n = (isOpen, background, hasDismissButton) => {
9142
9237
  zIndex: 0, // controls layering + creates new stacking context (prevents content within to be above other dialog areas)
9143
9238
  },
9144
9239
  },
9145
- ...getFunctionalComponentDialogBaseStyles(isOpen, 'shading'),
9240
+ dialog: getFunctionalComponentDialogBaseStyles(isOpen, 'shading'),
9146
9241
  },
9147
9242
  scroller: getScrollerJssStyle('fullscreen'),
9148
9243
  sheet: {
@@ -10280,7 +10375,7 @@ const getComponentCss$1 = () => {
10280
10375
  [cssVariablePositionBottomInternal]: ref(cssVariablePositionBottom, '56px'),
10281
10376
  position: 'fixed', // fallback for older browsers without native `popover` support
10282
10377
  inset: `auto ${gridExtendedOffsetBase} ${ref(cssVariablePositionBottomInternal)}`,
10283
- zIndex: TOAST_Z_INDEX, // fallback for older browsers without native `popover` support
10378
+ zIndex: 999999, // fallback for older browsers without native `popover` support
10284
10379
  [getMediaQueryMin('s')]: {
10285
10380
  [cssVariablePositionBottomInternal]: ref(cssVariablePositionBottom, '64px'),
10286
10381
  inset: `auto auto ${ref(cssVariablePositionBottomInternal)} 64px`,
@@ -10379,6 +10474,7 @@ exports.getDividerCss = getComponentCss$$;
10379
10474
  exports.getDrilldownCss = getComponentCss$_;
10380
10475
  exports.getDrilldownItemCss = getComponentCss$Z;
10381
10476
  exports.getDrilldownLinkCss = getComponentCss$Y;
10477
+ exports.getFCDismissButtonStyles = getFCDismissButtonStyles;
10382
10478
  exports.getFieldsetCss = getComponentCss$X;
10383
10479
  exports.getFlagCss = getComponentCss$W;
10384
10480
  exports.getFlyoutCss = getComponentCss$V;
@@ -3482,7 +3482,7 @@ const hasShowPickerSupport = () => (hasDocument &&
3482
3482
  'showPicker' in HTMLInputElement.prototype &&
3483
3483
  CSS.supports('selector(::-webkit-calendar-picker-indicator)'));
3484
3484
 
3485
- const prefix = `[Porsche Design System v${"4.4.0-rc.0"}]` // this part isn't covered by unit tests
3485
+ const prefix = `[Porsche Design System v${"4.4.0"}]` // this part isn't covered by unit tests
3486
3486
  ;
3487
3487
  const consoleError$1 = (...messages) => {
3488
3488
  console.error(prefix, ...messages);
@@ -1,14 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- var react = require('react');
5
- require('../../provider.cjs');
6
4
  var splitChildren = require('../../splitChildren.cjs');
5
+ var react = require('react');
7
6
  var minifyCss = require('../../minifyCss.cjs');
8
7
  var stylesEntry = require('../../../../../../components/dist/styles/esm/styles-entry.cjs');
9
8
  var utilsEntry = require('../../../../../../components/dist/utils/esm/utils-entry.cjs');
9
+ var fcDismissButton = require('./fc-dismiss-button.cjs');
10
10
  var notificationBase = require('./notification-base.cjs');
11
- var button_wrapper = require('../components/button.wrapper.cjs');
12
11
 
13
12
  /**
14
13
  * @slot {"name": "heading", "description": "Defines the heading of the banner. Can be used as an alternative to the `heading` prop for rich content." }
@@ -23,6 +22,12 @@ class DSRBanner extends react.Component {
23
22
  refDismiss;
24
23
  hasHeadingSlot;
25
24
  hasDescriptionSlot;
25
+ // Tracks whether the document-level Escape listener is currently registered (guards the idempotent sync below).
26
+ hasKeydownListener = false;
27
+ // Tracks the component's first render. While `true`, the entry transition (`@starting-style`) is suppressed so an
28
+ // initially-open banner (`open=true` on page load) appears instantly instead of sliding/fading in; flipped to `false`
29
+ // in `componentDidLoad`, so every later (user-triggered) open keeps the entry animation.
30
+ isInitialRender = true;
26
31
  topLayer = utilsEntry.createTopLayerController({
27
32
  getElement: () => this.props.refPopover,
28
33
  isShown: () => !!this.props.refPopover?.matches(':popover-open'),
@@ -35,8 +40,13 @@ class DSRBanner extends react.Component {
35
40
  const hasDescriptionSlot = namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0;
36
41
  const headingText = this.props.heading ? this.props.heading : namedSlotChildren.find(({ props: { slot } }) => slot === 'heading')?.props.children;
37
42
  const style = minifyCss.minifyCss(stylesEntry.getBannerCss(this.props.open, this.props.position, this.props.state, this.props.dismissButton, !!(this.props.heading || hasHeadingSlot)));
38
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxRuntime.jsx("div", { popover: "manual", "aria-hidden": this.props.open ? 'false' : 'true', ...utilsEntry.getBannerAriaAttributes(this.props.state, headingText), children: jsxRuntime.jsx(notificationBase.NotificationBase, { heading: this.props.heading, headingTag: this.props.headingTag, hasHeadingSlot: hasHeadingSlot, description: this.props.description, hasDescriptionSlot: hasDescriptionSlot, ...(this.props.dismissButton && {
39
- dismissButton: (jsxRuntime.jsx(button_wrapper.PButton, { className: "dismiss", type: "button", variant: "secondary", icon: "close", hideLabel: true, compact: true, ...(headingText ? { aria: { 'aria-description': headingText } } : {}), children: "Close banner" })),
43
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style + (this.props.open ? '[popover]{display:grid!important}' : '') } }), jsxRuntime.jsx("div", { popover: "manual",
44
+ // `inert` (not `aria-hidden`) removes the panel from the a11y tree AND prevents focus while closed / during the
45
+ // fade-out. Using `aria-hidden` here triggers a browser warning when a focusable descendant still holds focus
46
+ // during the closing transition ("Blocked aria-hidden on an element because its descendant retained focus").
47
+ // `inert` avoids that and mirrors the pattern used by `p-modal` / `p-popover` / `p-drilldown`.
48
+ inert: !this.props.open, ...utilsEntry.getBannerAriaAttributes(this.props.state, headingText), children: jsxRuntime.jsx(notificationBase.NotificationBase, { heading: this.props.heading, headingTag: this.props.headingTag, hasHeadingSlot: hasHeadingSlot, description: this.props.description, hasDescriptionSlot: hasDescriptionSlot, ...(this.props.dismissButton && {
49
+ dismissButton: (jsxRuntime.jsx(fcDismissButton.FCDismissButton, { label: "Close banner", ariaDescription: headingText || undefined })),
40
50
  }) }) })] }), this.props.children] }));
41
51
  }
42
52
  }
@@ -1,14 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- require('react');
5
- require('../../provider.cjs');
6
- var button_wrapper = require('../components/button.wrapper.cjs');
4
+ var fcDismissButton = require('./fc-dismiss-button.cjs');
7
5
 
8
- const DialogBase = ({ children,
9
- // host,
10
- inert, containerClass, ariaAttributes, dismissable = false, header, footer, subFooter, }) => {
11
- return (jsxRuntime.jsx("dialog", { inert: inert, tabIndex: -1, ...(ariaAttributes ?? {}), children: jsxRuntime.jsx("div", { className: "scroller", children: jsxRuntime.jsxs("div", { className: containerClass, children: [dismissable && (jsxRuntime.jsx(button_wrapper.PButton, { className: "dismiss", compact: true, type: "button", hideLabel: true, icon: "close", children: `Dismiss ${containerClass}` })), header, children, footer, subFooter] }) }) }));
6
+ const DialogBase = ({ children, inert, containerClass, ariaAttributes, onMouseDown, dismissable = false, header, footer, subFooter, }) => {
7
+ return (jsxRuntime.jsx("dialog", { inert: inert, tabIndex: -1, ...(ariaAttributes ?? {}), children: jsxRuntime.jsx("div", { className: "scroller", children: jsxRuntime.jsxs("div", { className: containerClass, children: [dismissable && jsxRuntime.jsx(fcDismissButton.FCDismissButton, { label: `Dismiss ${containerClass}` }), header, children, footer, subFooter] }) }) }));
12
8
  };
13
9
 
14
10
  exports.DialogBase = DialogBase;
@@ -26,6 +26,9 @@ class DSRDrilldown extends react.Component {
26
26
  dialog;
27
27
  drawer;
28
28
  isDesktop = false;
29
+ // Tracks whether the current pointer gesture started inside the drawer (not on the backdrop). Lets `onClickDialog`
30
+ // skip dismissal when a selection is dragged out of the drawer and released on the backdrop.
31
+ isPointerDownInside = false;
29
32
  async componentWillLoad() {
30
33
  this.props.defineDrilldownItemElements();
31
34
  const activeItem = this.props.drilldownItemElements.find((item) => item.identifier === this.props.activeIdentifier);
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ /**
6
+ * Shared dismiss/close button used across dialog-based (flyout/modal/sheet) and notification-based
7
+ * (banner/inline-notification/toast) components. Renders a native `<button className="dismiss">` with the close icon
8
+ * inlined as a CSS mask (see `fc-dismiss-button-styles.ts`) — so it needs neither a nested `p-button` nor a `p-icon`
9
+ * (avoiding the async CDN icon fetch). Each consumer supplies the variant/positioning styling via the `.dismiss` class.
10
+ * The visible label text is visually hidden but exposed to assistive technology as the accessible name.
11
+ */
12
+ const FCDismissButton = ({ label, ariaDescription, }) => {
13
+ return (jsxRuntime.jsx("button", { className: "dismiss", type: "button", ...(ariaDescription ? { 'aria-description': ariaDescription } : {}), children: jsxRuntime.jsx("span", { children: label }) }));
14
+ };
15
+
16
+ exports.FCDismissButton = FCDismissButton;
@@ -25,6 +25,9 @@ class DSRFlyout extends react.Component {
25
25
  hasHeader;
26
26
  hasFooter;
27
27
  hasSubFooter;
28
+ // Tracks whether the current pointer gesture started inside the panel (not on the backdrop). Lets `onClickDialog`
29
+ // skip dismissal when a selection is dragged out of the panel and released on the backdrop.
30
+ isPointerDownInside = false;
28
31
  topLayer = utilsEntry.createTopLayerController({
29
32
  getElement: () => this.props.dialog,
30
33
  isShown: () => !!this.props.dialog?.open,
@@ -37,7 +40,17 @@ class DSRFlyout extends react.Component {
37
40
  const hasFooter = namedSlotChildren.filter(({ props: { slot } }) => slot === 'footer').length > 0;
38
41
  const hasSubFooter = namedSlotChildren.filter(({ props: { slot } }) => slot === 'sub-footer').length > 0;
39
42
  const style = minifyCss.minifyCss(stylesEntry.getFlyoutCss(this.props.open, this.props.background, this.props.backdrop, this.props.position, hasHeader, hasFooter, hasSubFooter, this.props.footerBehavior, this.props.fullscreen));
40
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxRuntime.jsx(dialogBase.DialogBase, { host: null, dismissable: true, containerClass: "flyout", header: hasHeader ? jsxRuntime.jsx("slot", { name: "header" }) : undefined, footer: hasFooter ? jsxRuntime.jsx("slot", { name: "footer" }) : undefined, subFooter: hasSubFooter ? jsxRuntime.jsx("slot", { name: "sub-footer" }) : undefined, ariaAttributes: utilsEntry.parseAndGetAriaAttributes({
43
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxRuntime.jsx(dialogBase.DialogBase
44
+ // `inert` (not `aria-hidden`) removes the panel from the a11y tree AND prevents focus while closed / during the
45
+ // fade-out. Using `aria-hidden` here triggers a browser warning when a focusable descendant still holds focus
46
+ // during the closing transition ("Blocked aria-hidden on an element because its descendant retained focus").
47
+ // `inert` avoids that and mirrors the pattern used by `p-modal` / `p-popover` / `p-drilldown`.
48
+ , {
49
+ // `inert` (not `aria-hidden`) removes the panel from the a11y tree AND prevents focus while closed / during the
50
+ // fade-out. Using `aria-hidden` here triggers a browser warning when a focusable descendant still holds focus
51
+ // during the closing transition ("Blocked aria-hidden on an element because its descendant retained focus").
52
+ // `inert` avoids that and mirrors the pattern used by `p-modal` / `p-popover` / `p-drilldown`.
53
+ inert: !this.props.open, dismissable: true, containerClass: "flyout", header: hasHeader ? jsxRuntime.jsx("slot", { name: "header" }) : undefined, footer: hasFooter ? jsxRuntime.jsx("slot", { name: "footer" }) : undefined, subFooter: hasSubFooter ? jsxRuntime.jsx("slot", { name: "sub-footer" }) : undefined, ariaAttributes: utilsEntry.parseAndGetAriaAttributes({
41
54
  'aria-modal': true,
42
55
  ...{ 'aria-label': hasHeader ? namedSlotChildren.find(({ props: { slot } }) => slot === 'header')?.props.children : 'Flyout' },
43
56
  ...utilsEntry.parseAndGetAriaAttributes(this.props.aria),