@porsche-design-system/components-react 3.18.0 → 3.19.0-rc.1

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 (24) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/cjs/lib/components/canvas.wrapper.cjs +3 -5
  3. package/cjs/lib/components/carousel.wrapper.cjs +3 -3
  4. package/esm/lib/components/canvas.wrapper.d.ts +13 -21
  5. package/esm/lib/components/canvas.wrapper.mjs +4 -6
  6. package/esm/lib/components/carousel.wrapper.d.ts +17 -1
  7. package/esm/lib/components/carousel.wrapper.mjs +3 -3
  8. package/esm/lib/types.d.ts +8 -7
  9. package/package.json +2 -2
  10. package/ssr/cjs/components/dist/styles/esm/styles-entry.cjs +244 -63
  11. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/components/canvas.wrapper.cjs +4 -6
  12. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/components/carousel.wrapper.cjs +4 -4
  13. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/canvas.cjs +16 -21
  14. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/carousel.cjs +3 -2
  15. package/ssr/esm/components/dist/styles/esm/styles-entry.mjs +244 -63
  16. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/canvas.wrapper.mjs +5 -7
  17. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/carousel.wrapper.mjs +4 -4
  18. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/canvas.mjs +16 -21
  19. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/carousel.mjs +3 -2
  20. package/ssr/esm/lib/components/canvas.wrapper.d.ts +13 -21
  21. package/ssr/esm/lib/components/carousel.wrapper.d.ts +17 -1
  22. package/ssr/esm/lib/dsr-components/canvas.d.ts +5 -4
  23. package/ssr/esm/lib/dsr-components/carousel.d.ts +1 -0
  24. package/ssr/esm/lib/types.d.ts +8 -7
@@ -1,15 +1,15 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import '../components/accordion.wrapper.mjs';
3
3
  import '../components/banner.wrapper.mjs';
4
- import '../components/button.wrapper.mjs';
4
+ import { PButton } from '../components/button.wrapper.mjs';
5
5
  import '../components/button-group.wrapper.mjs';
6
- import { PButtonPure } from '../components/button-pure.wrapper.mjs';
6
+ import '../components/button-pure.wrapper.mjs';
7
7
  import '../components/button-tile.wrapper.mjs';
8
8
  import '../components/canvas.wrapper.mjs';
9
9
  import '../components/carousel.wrapper.mjs';
10
10
  import '../components/checkbox-wrapper.wrapper.mjs';
11
11
  import '../components/content-wrapper.wrapper.mjs';
12
- import '../components/crest.wrapper.mjs';
12
+ import { PCrest } from '../components/crest.wrapper.mjs';
13
13
  import '../components/display.wrapper.mjs';
14
14
  import '../components/divider.wrapper.mjs';
15
15
  import '../components/fieldset.wrapper.mjs';
@@ -70,7 +70,7 @@ import '../components/text-list-item.wrapper.mjs';
70
70
  import '../components/textarea.wrapper.mjs';
71
71
  import '../components/textarea-wrapper.wrapper.mjs';
72
72
  import '../components/toast.wrapper.mjs';
73
- import '../components/wordmark.wrapper.mjs';
73
+ import { PWordmark } from '../components/wordmark.wrapper.mjs';
74
74
  import { splitChildren } from '../../splitChildren.mjs';
75
75
  import { Component } from 'react';
76
76
  import { minifyCss } from '../../minifyCss.mjs';
@@ -78,40 +78,35 @@ import { stripFocusAndHoverStyles } from '../../stripFocusAndHoverStyles.mjs';
78
78
  import { getCanvasCss as getComponentCss$12 } from '../../../../../../components/dist/styles/esm/styles-entry.mjs';
79
79
 
80
80
  /**
81
- * @slot {"name": "header", "description": "Renders a **sticky** header section above the content area." }
81
+ * @slot {"name": "header-start", "description": "Renders a **sticky** header section above the content area on the **start** side (**left** in **LTR** mode / **right** in **RTL** mode)." }
82
+ * @slot {"name": "header-end", "description": "Renders a **sticky** header section above the content area on the **end** side (**right** in **LTR** mode / **left** in **RTL** mode)." }
82
83
  * @slot {"name": "", "description": "Default slot for the main content" }
84
+ * @slot {"name": "title", "description": "Application name" }
83
85
  * @slot {"name": "footer", "description": "Shows a footer section, flowing under the content area when scrollable." }
84
86
  * @slot {"name": "sidebar-start", "description": "Shows a sidebar area on the **start** side (**left** in **LTR** mode / **right** in **RTL** mode). On mobile view it transforms into a flyout." }
85
87
  * @slot {"name": "sidebar-end", "description": "Shows a sidebar area on the **end** side (**right** in **LTR** mode / **left** in **RTL** mode). On mobile view it transforms into a flyout." }
86
88
  *
87
- * @controlled {"props": ["sidebarStartOpen"], "event": "dismissSidebarStart"}
88
- * @controlled {"props": ["sidebarEndOpen"], "event": "dismissSidebarEnd"}
89
- *
90
89
  * @experimental
91
90
  */
92
91
  class DSRCanvas extends Component {
93
92
  host;
94
93
  isDesktopView = false;
94
+ hasSidebarStart;
95
+ hasSidebarEnd;
95
96
  render() {
96
- splitChildren(this.props.children);
97
- const style = minifyCss(stripFocusAndHoverStyles(getComponentCss$12(this.props.sidebarStartOpen, this.props.sidebarStartWidth, this.props.sidebarEndOpen, this.props.sidebarEndWidth)));
98
- return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxs(Fragment, { children: [jsxs("div", { className: "canvas", children: [jsx("header", { part: "header", children: jsx("slot", { name: "header" }) }), jsx("main", { part: "main", children: jsx("slot", {}) }), jsx("footer", { part: "footer", children: jsx("slot", { name: "footer" }) }), this.props.isDesktopView && (jsxs(Fragment, { children: [jsxs("aside", {
99
- // "part" is not valid in TS
100
- // eslint-disable-next-line
101
- /* @ts-ignore */
102
- part: "sidebar-start",
97
+ const { children, namedSlotChildren, otherChildren } = splitChildren(this.props.children);
98
+ const hasSidebarStart = namedSlotChildren.filter(({ props: { slot } }) => slot === 'sidebar-start').length > 0;
99
+ const hasSidebarEnd = namedSlotChildren.filter(({ props: { slot } }) => slot === 'sidebar-end').length > 0;
100
+ const style = minifyCss(stripFocusAndHoverStyles(getComponentCss$12(this.props.theme, this.props.sidebarStartOpen, this.props.sidebarEndOpen)));
101
+ return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxs(Fragment, { children: [jsxs("div", { className: "canvas", children: [jsxs("header", { children: [jsxs("div", { className: "header", children: [hasSidebarStart && (jsxs(PButton, { theme: this.props.theme, icon: this.props.sidebarStartIcon, variant: "ghost", compact: true, "hide-label": "true", aria: { 'aria-expanded': this.props.sidebarStartOpen }, children: [this.props.sidebarStartOpen ? 'Close' : 'Open', " navigation sidebar"] })), jsx("h2", { children: jsx("slot", { name: "title" }) }), jsx("slot", { name: "header-start" })] }), jsx(PCrest, { className: "crest" }), jsx(PWordmark, { className: "wordmark", size: "inherit", theme: this.props.theme }), jsxs("div", { className: "header", children: [jsx("slot", { name: "header-end" }), hasSidebarEnd && (jsxs(PButton, { theme: this.props.theme, icon: this.props.sidebarEndIcon, variant: "ghost", compact: true, "hide-label": "true", aria: { 'aria-expanded': this.props.sidebarEndOpen }, children: [this.props.sidebarEndOpen ? 'Close' : 'Open', " settings sidebar"] }))] })] }), jsx("main", { children: jsx("slot", {}) }), jsx("footer", { children: jsx("slot", { name: "footer" }) }), this.props.isDesktopView && (jsxs(Fragment, { children: [hasSidebarStart && (jsx("aside", { className: "sidebar-start",
103
102
  // "inert" will be known from React 19 onwards, see https://github.com/facebook/react/pull/24730
104
103
  // eslint-disable-next-line
105
104
  /* @ts-ignore */
106
- inert: this.props.sidebarStartOpen ? null : true, children: [jsx(PButtonPure, { className: "close", icon: "close", hideLabel: true, children: "Close Sidebar" }), jsx("slot", { name: "sidebar-start" })] }), jsxs("aside", {
107
- // "part" is not valid in TS
108
- // eslint-disable-next-line
109
- /* @ts-ignore */
110
- part: "sidebar-end",
105
+ inert: this.props.sidebarStartOpen ? null : true, "aria-label": `Navigation sidebar ${this.props.sidebarStartOpen ? 'open' : 'closed'}`, children: jsx("slot", { name: "sidebar-start" }) })), hasSidebarEnd && (jsx("aside", { className: "sidebar-end",
111
106
  // "inert" will be known from React 19 onwards, see https://github.com/facebook/react/pull/24730
112
107
  // eslint-disable-next-line
113
108
  /* @ts-ignore */
114
- inert: this.props.sidebarEndOpen ? null : true, children: [jsx(PButtonPure, { className: "close", icon: "close", hideLabel: true, children: "Close Sidebar" }), jsx("slot", { name: "sidebar-end" })] })] }))] }), !this.props.isDesktopView && (jsxs(Fragment, { children: [jsx(PFlyout, { open: this.props.sidebarStartOpen, position: "start", children: jsx("slot", { name: "sidebar-start" }) }), jsx(PFlyout, { open: this.props.sidebarEndOpen, position: "end", children: jsx("slot", { name: "sidebar-end" }) })] }))] })] }), this.props.children] }));
109
+ inert: this.props.sidebarEndOpen ? null : true, "aria-label": `Settings sidebar ${this.props.sidebarEndOpen ? 'open' : 'closed'}`, children: jsx("slot", { name: "sidebar-end" }) }))] }))] }), !this.props.isDesktopView && (jsxs(Fragment, { children: [hasSidebarStart && (jsx(PFlyout, { theme: this.props.theme, open: this.props.sidebarStartOpen, position: "start", children: jsx("slot", { name: "sidebar-start" }) })), hasSidebarEnd && (jsx(PFlyout, { theme: this.props.theme, open: this.props.sidebarEndOpen, position: "end", children: jsx("slot", { name: "sidebar-end" }) }))] }))] })] }), this.props.children] }));
115
110
  }
116
111
  }
117
112
 
@@ -114,13 +114,13 @@ class DSRCarousel extends Component {
114
114
  // 'aria-controls': 'splide-track', // TODO: cross shadow dom? use native button tag instead of p-button-pure?
115
115
  };
116
116
  const headingId = 'heading';
117
- const style = minifyCss(stripFocusAndHoverStyles(getComponentCss$11(hasHeadingPropOrSlot, hasDescriptionPropOrSlot, hasControlsSlot, this.props.headingSize, this.props.width,
117
+ const style = minifyCss(stripFocusAndHoverStyles(getComponentCss$11(this.props.gradientColor, hasHeadingPropOrSlot, hasDescriptionPropOrSlot, hasControlsSlot, this.props.headingSize, this.props.width,
118
118
  // flip boolean values of disablePagination since it is the inverse of pagination
119
119
  this.props.disablePagination
120
120
  ? typeof this.props.disablePagination === 'object'
121
121
  ? Object.fromEntries(Object.entries(this.props.disablePagination).map(([key, value]) => [key, !value]))
122
122
  : !this.props.disablePagination
123
- : this.props.pagination, isInfinitePagination(this.props.amountOfPages), (alignHeaderDeprecationMap[this.props.alignHeader] ||
123
+ : this.props.pagination, isInfinitePagination(this.props.focusOnCenterSlide ? this.slides.length : this.props.amountOfPages), (alignHeaderDeprecationMap[this.props.alignHeader] ||
124
124
  this.props.alignHeader), this.props.theme, (this.props.slidesPerPage === 'auto' || typeof this.props.slidesPerPage === 'object' || this.props.slidesPerPage < otherChildren.length))));
125
125
  return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxs(Fragment, { children: [jsxs("div", { className: "header", children: [hasHeadingPropOrSlot &&
126
126
  (this.props.heading ? (jsx("h2", { className: "heading", id: headingId, children: this.props.heading })) : (jsx("div", { className: "heading", id: headingId, children: jsx("slot", { name: "heading" }) }))), hasDescriptionPropOrSlot && (this.props.description ? jsx("p", { children: this.props.description }) : jsx("slot", { name: "description" })), hasControlsSlot && jsx("slot", { name: "controls" }), jsxs("div", { className: "nav", children: [this.props.skipLinkTarget && (jsx(PLinkPure, { href: this.props.skipLinkTarget, theme: this.props.theme, icon: "arrow-last", className: "btn skip-link", alignLabel: "start", hideLabel: true, children: "Skip carousel entries" })), (this.props.slidesPerPage === 'auto' || typeof this.props.slidesPerPage === 'object' || this.props.slidesPerPage < otherChildren.length) && (jsx(PButtonPure, { ...btnProps, icon: "arrow-left" })), (this.props.slidesPerPage === 'auto' || typeof this.props.slidesPerPage === 'object' || this.props.slidesPerPage < otherChildren.length) && (jsx(PButtonPure, { ...btnProps, icon: "arrow-right" }))] })] }), jsx("div", { id: "splide", className: "splide", ...parseAndGetAriaAttributes({
@@ -128,6 +128,7 @@ class DSRCarousel extends Component {
128
128
  ...parseAndGetAriaAttributes(this.props.aria),
129
129
  }), children: jsx("div", { className: "splide__track", children: jsx("div", { className: "splide__list", children: otherChildren.map((_, i) => (jsx("div", { className: "splide__slide", children: jsx("slot", { name: `slide-${i}` }) }, i))) }) }) }), (this.props.disablePagination ? this.props.disablePagination !== true : this.props.pagination) && (this.props.slidesPerPage === 'auto' || typeof this.props.slidesPerPage === 'object' || this.props.slidesPerPage < otherChildren.length) && (jsx("div", { className: "pagination-container", "aria-hidden": "true", children: jsx("div", { className: "pagination" }) }))] })] }), this.props.children] }));
130
130
  }
131
+ getPageCount = () => (this.props.focusOnCenterSlide ? this.slides.length : this.props.amountOfPages);
131
132
  }
132
133
 
133
134
  export { DSRCarousel };
@@ -1,56 +1,48 @@
1
1
  import type { BaseProps } from '../../BaseProps';
2
- import type { CanvasSidebarEndWidth, CanvasSidebarStartWidth } from '../types';
2
+ import type { CanvasSidebarEndIcon, CanvasSidebarStartIcon, Theme } from '../types';
3
3
  export type PCanvasProps = BaseProps & {
4
4
  /**
5
- * Emitted when the component requests to close the sidebar on the end side.
5
+ * The icon to toggle the Sidebar on the end side
6
6
  */
7
- onDismissSidebarEnd?: (event: CustomEvent<void>) => void;
8
- /**
9
- * Emitted when the component requests to close the sidebar on the start side.
10
- */
11
- onDismissSidebarStart?: (event: CustomEvent<void>) => void;
7
+ sidebarEndIcon?: CanvasSidebarEndIcon;
12
8
  /**
13
9
  * Open Sidebar on the end side
14
10
  */
15
11
  sidebarEndOpen?: boolean;
16
12
  /**
17
- * Defines the width of the sidebar on the end side
13
+ * The icon to toggle the Sidebar on the start side
18
14
  */
19
- sidebarEndWidth?: CanvasSidebarEndWidth;
15
+ sidebarStartIcon?: CanvasSidebarStartIcon;
20
16
  /**
21
17
  * Open Sidebar on the start side
22
18
  */
23
19
  sidebarStartOpen?: boolean;
24
20
  /**
25
- * Defines the width of the sidebar on the start side
21
+ * Adapts the color depending on the theme. Has no effect when "inherit" is set as color prop.
26
22
  */
27
- sidebarStartWidth?: CanvasSidebarStartWidth;
23
+ theme?: Theme;
28
24
  };
29
25
  export declare const PCanvas: import("react").ForwardRefExoticComponent<import("react").DOMAttributes<{}> & Pick<import("react").HTMLAttributes<{}>, "suppressHydrationWarning" | "autoFocus" | "className" | "dir" | "hidden" | "id" | "lang" | "slot" | "style" | "tabIndex" | "title" | "translate" | "role"> & {
30
26
  /**
31
- * Emitted when the component requests to close the sidebar on the end side.
32
- */
33
- onDismissSidebarEnd?: (event: CustomEvent<void>) => void;
34
- /**
35
- * Emitted when the component requests to close the sidebar on the start side.
27
+ * The icon to toggle the Sidebar on the end side
36
28
  */
37
- onDismissSidebarStart?: (event: CustomEvent<void>) => void;
29
+ sidebarEndIcon?: CanvasSidebarEndIcon;
38
30
  /**
39
31
  * Open Sidebar on the end side
40
32
  */
41
33
  sidebarEndOpen?: boolean;
42
34
  /**
43
- * Defines the width of the sidebar on the end side
35
+ * The icon to toggle the Sidebar on the start side
44
36
  */
45
- sidebarEndWidth?: CanvasSidebarEndWidth;
37
+ sidebarStartIcon?: CanvasSidebarStartIcon;
46
38
  /**
47
39
  * Open Sidebar on the start side
48
40
  */
49
41
  sidebarStartOpen?: boolean;
50
42
  /**
51
- * Defines the width of the sidebar on the start side
43
+ * Adapts the color depending on the theme. Has no effect when "inherit" is set as color prop.
52
44
  */
53
- sidebarStartWidth?: CanvasSidebarStartWidth;
45
+ theme?: Theme;
54
46
  } & {
55
47
  children?: import("react").ReactNode | undefined;
56
48
  } & import("react").RefAttributes<HTMLElement>>;
@@ -1,5 +1,5 @@
1
1
  import type { BaseProps } from '../../BaseProps';
2
- import type { CarouselAlignHeader, SelectedAriaAttributes, CarouselAriaAttribute, BreakpointCustomizable, CarouselHeadingSize, CarouselInternationalization, CarouselUpdateEventDetail, Theme, CarouselWidth } from '../types';
2
+ import type { CarouselAlignHeader, SelectedAriaAttributes, CarouselAriaAttribute, BreakpointCustomizable, CarouselGradientColor, CarouselHeadingSize, CarouselInternationalization, CarouselUpdateEventDetail, Theme, CarouselWidth } from '../types';
3
3
  export type PCarouselProps = BaseProps & {
4
4
  /**
5
5
  * Defines which slide to be active (zero-based numbering).
@@ -21,6 +21,14 @@ export type PCarouselProps = BaseProps & {
21
21
  * @deprecated since v3.0.0, will be removed with next major release, use `pagination` instead. If true, the carousel will not show pagination bullets at the bottom.
22
22
  */
23
23
  disablePagination?: BreakpointCustomizable<boolean>;
24
+ /**
25
+ * Indicates whether focus should be set on the center slide. If true, the carousel loops by individual slide; otherwise, it loops by page.
26
+ */
27
+ focusOnCenterSlide?: boolean;
28
+ /**
29
+ * Adapts the background gradient for the left and right edge.
30
+ */
31
+ gradientColor?: CarouselGradientColor;
24
32
  /**
25
33
  * Defines the heading used in the carousel.
26
34
  */
@@ -92,6 +100,14 @@ export declare const PCarousel: import("react").ForwardRefExoticComponent<import
92
100
  * @deprecated since v3.0.0, will be removed with next major release, use `pagination` instead. If true, the carousel will not show pagination bullets at the bottom.
93
101
  */
94
102
  disablePagination?: BreakpointCustomizable<boolean>;
103
+ /**
104
+ * Indicates whether focus should be set on the center slide. If true, the carousel loops by individual slide; otherwise, it loops by page.
105
+ */
106
+ focusOnCenterSlide?: boolean;
107
+ /**
108
+ * Adapts the background gradient for the left and right edge.
109
+ */
110
+ gradientColor?: CarouselGradientColor;
95
111
  /**
96
112
  * Defines the heading used in the carousel.
97
113
  */
@@ -1,18 +1,19 @@
1
1
  import { Component } from 'react';
2
2
  /**
3
- * @slot {"name": "header", "description": "Renders a **sticky** header section above the content area." }
3
+ * @slot {"name": "header-start", "description": "Renders a **sticky** header section above the content area on the **start** side (**left** in **LTR** mode / **right** in **RTL** mode)." }
4
+ * @slot {"name": "header-end", "description": "Renders a **sticky** header section above the content area on the **end** side (**right** in **LTR** mode / **left** in **RTL** mode)." }
4
5
  * @slot {"name": "", "description": "Default slot for the main content" }
6
+ * @slot {"name": "title", "description": "Application name" }
5
7
  * @slot {"name": "footer", "description": "Shows a footer section, flowing under the content area when scrollable." }
6
8
  * @slot {"name": "sidebar-start", "description": "Shows a sidebar area on the **start** side (**left** in **LTR** mode / **right** in **RTL** mode). On mobile view it transforms into a flyout." }
7
9
  * @slot {"name": "sidebar-end", "description": "Shows a sidebar area on the **end** side (**right** in **LTR** mode / **left** in **RTL** mode). On mobile view it transforms into a flyout." }
8
10
  *
9
- * @controlled {"props": ["sidebarStartOpen"], "event": "dismissSidebarStart"}
10
- * @controlled {"props": ["sidebarEndOpen"], "event": "dismissSidebarEnd"}
11
- *
12
11
  * @experimental
13
12
  */
14
13
  export declare class DSRCanvas extends Component<any> {
15
14
  host: HTMLElement;
16
15
  private isDesktopView;
16
+ private hasSidebarStart;
17
+ private hasSidebarEnd;
17
18
  render(): JSX.Element;
18
19
  }
@@ -18,4 +18,5 @@ export declare class DSRCarousel extends Component<any> {
18
18
  private slides;
19
19
  private get hasNavigation();
20
20
  render(): JSX.Element;
21
+ private getPageCount;
21
22
  }
@@ -684,18 +684,19 @@ export type ButtonTileSize = TileSize;
684
684
  export type ButtonTileBackground = TileBackground;
685
685
  export type ButtonTileWeight = TileWeight;
686
686
  export type ButtonTileAlign = TileAlign;
687
- declare const CANVAS_SIDEBAR_WIDTHS: readonly [
688
- "medium",
689
- "large"
690
- ];
691
- export type CanvasSidebarWidth = (typeof CANVAS_SIDEBAR_WIDTHS)[number];
692
- export type CanvasSidebarStartWidth = CanvasSidebarWidth;
693
- export type CanvasSidebarEndWidth = CanvasSidebarWidth;
687
+ export type CanvasSidebarStartIcon = IconName;
688
+ export type CanvasSidebarEndIcon = IconName;
694
689
  declare const CAROUSEL_WIDTHS: readonly [
695
690
  "basic",
696
691
  "extended"
697
692
  ];
698
693
  export type CarouselWidth = (typeof CAROUSEL_WIDTHS)[number];
694
+ declare const CAROUSEL_GRADIENT_COLORS: readonly [
695
+ "background-base",
696
+ "background-surface",
697
+ "none"
698
+ ];
699
+ export type CarouselGradientColor = (typeof CAROUSEL_GRADIENT_COLORS)[number];
699
700
  declare const CAROUSEL_ALIGN_HEADERS: readonly [
700
701
  "start",
701
702
  "center",