@lumx/react 3.16.0 → 3.16.1-alpha.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.
package/package.json CHANGED
@@ -6,8 +6,8 @@
6
6
  "url": "https://github.com/lumapps/design-system/issues"
7
7
  },
8
8
  "dependencies": {
9
- "@lumx/core": "^3.16.0",
10
- "@lumx/icons": "^3.16.0",
9
+ "@lumx/core": "^3.16.1-alpha.1",
10
+ "@lumx/icons": "^3.16.1-alpha.1",
11
11
  "@popperjs/core": "^2.5.4",
12
12
  "body-scroll-lock": "^3.1.5",
13
13
  "classnames": "^2.3.2",
@@ -105,5 +105,5 @@
105
105
  "build:storybook": "storybook build"
106
106
  },
107
107
  "sideEffects": false,
108
- "version": "3.16.0"
108
+ "version": "3.16.1-alpha.1"
109
109
  }
@@ -1,5 +1,4 @@
1
1
  import React, { Children, ReactElement, ReactNode, Ref, RefObject, useMemo, useRef, useState } from 'react';
2
- import { createPortal } from 'react-dom';
3
2
 
4
3
  import classNames from 'classnames';
5
4
 
@@ -20,6 +19,7 @@ import { forwardRef } from '@lumx/react/utils/react/forwardRef';
20
19
  import { useDisableBodyScroll } from '@lumx/react/hooks/useDisableBodyScroll';
21
20
  import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibility';
22
21
  import { ThemeProvider } from '@lumx/react/utils/theme/ThemeContext';
22
+ import { Portal } from '@lumx/react/utils/PortalProvider';
23
23
 
24
24
  /**
25
25
  * Defines the props of the component.
@@ -193,95 +193,94 @@ export const Dialog = forwardRef<DialogProps, HTMLDivElement>((props, ref) => {
193
193
 
194
194
  const shouldPreventCloseOnClickAway = preventAutoClose || preventCloseOnClick;
195
195
 
196
- return isOpen || isVisible
197
- ? createPortal(
198
- <div
199
- ref={mergeRefs(rootRef, ref)}
200
- {...forwardedProps}
201
- className={classNames(
202
- className,
203
- handleBasicClasses({
204
- isHidden: !isOpen,
205
- isLoading,
206
- isShown: isOpen || isVisible,
207
- prefix: CLASSNAME,
208
- size,
209
- }),
210
- )}
211
- style={{ zIndex }}
212
- >
213
- <div className={`${CLASSNAME}__overlay`} />
214
-
215
- <HeadingLevelProvider level={2}>
216
- <ThemeProvider value={undefined}>
217
- <div className={`${CLASSNAME}__container`} role="dialog" aria-modal="true" {...dialogProps}>
218
- <ClickAwayProvider
219
- callback={!shouldPreventCloseOnClickAway && onClose}
220
- childrenRefs={clickAwayRefs}
221
- parentRef={rootRef}
222
- >
223
- <section className={`${CLASSNAME}__wrapper`} ref={wrapperRef}>
224
- {(header || headerChildContent) && (
225
- <header
226
- {...headerChildProps}
227
- className={classNames(
228
- `${CLASSNAME}__header`,
229
- (forceHeaderDivider || hasTopIntersection) &&
230
- `${CLASSNAME}__header--has-divider`,
231
- headerChildProps?.className,
232
- )}
233
- >
234
- {header}
235
- {headerChildContent}
236
- </header>
237
- )}
238
-
239
- <div
240
- ref={mergeRefs(contentRef, localContentRef)}
241
- className={`${CLASSNAME}__content`}
242
- >
243
- <div
244
- className={`${CLASSNAME}__sentinel ${CLASSNAME}__sentinel--top`}
245
- ref={setSentinelTop}
246
- />
247
-
248
- {content}
249
-
250
- <div
251
- className={`${CLASSNAME}__sentinel ${CLASSNAME}__sentinel--bottom`}
252
- ref={setSentinelBottom}
253
- />
254
- </div>
255
-
256
- {(footer || footerChildContent) && (
257
- <footer
258
- {...footerChildProps}
259
- className={classNames(
260
- `${CLASSNAME}__footer`,
261
- (forceFooterDivider || hasBottomIntersection) &&
262
- `${CLASSNAME}__footer--has-divider`,
263
- footerChildProps?.className,
264
- )}
265
- >
266
- {footer}
267
- {footerChildContent}
268
- </footer>
269
- )}
270
-
271
- {isLoading && (
272
- <div className={`${CLASSNAME}__progress-overlay`}>
273
- <Progress variant={ProgressVariant.circular} />
274
- </div>
275
- )}
276
- </section>
277
- </ClickAwayProvider>
278
- </div>
279
- </ThemeProvider>
280
- </HeadingLevelProvider>
281
- </div>,
282
- document.body,
283
- )
284
- : null;
196
+ return isOpen || isVisible ? (
197
+ <Portal>
198
+ <div
199
+ ref={mergeRefs(rootRef, ref)}
200
+ {...forwardedProps}
201
+ className={classNames(
202
+ className,
203
+ handleBasicClasses({
204
+ isHidden: !isOpen,
205
+ isLoading,
206
+ isShown: isOpen || isVisible,
207
+ prefix: CLASSNAME,
208
+ size,
209
+ }),
210
+ )}
211
+ style={{ zIndex }}
212
+ >
213
+ <div className={`${CLASSNAME}__overlay`} />
214
+
215
+ <HeadingLevelProvider level={2}>
216
+ <ThemeProvider value={undefined}>
217
+ <div className={`${CLASSNAME}__container`} role="dialog" aria-modal="true" {...dialogProps}>
218
+ <ClickAwayProvider
219
+ callback={!shouldPreventCloseOnClickAway && onClose}
220
+ childrenRefs={clickAwayRefs}
221
+ parentRef={rootRef}
222
+ >
223
+ <section className={`${CLASSNAME}__wrapper`} ref={wrapperRef}>
224
+ {(header || headerChildContent) && (
225
+ <header
226
+ {...headerChildProps}
227
+ className={classNames(
228
+ `${CLASSNAME}__header`,
229
+ (forceHeaderDivider || hasTopIntersection) &&
230
+ `${CLASSNAME}__header--has-divider`,
231
+ headerChildProps?.className,
232
+ )}
233
+ >
234
+ {header}
235
+ {headerChildContent}
236
+ </header>
237
+ )}
238
+
239
+ <div
240
+ ref={mergeRefs(contentRef, localContentRef)}
241
+ className={`${CLASSNAME}__content`}
242
+ >
243
+ <div
244
+ className={`${CLASSNAME}__sentinel ${CLASSNAME}__sentinel--top`}
245
+ ref={setSentinelTop}
246
+ />
247
+
248
+ {content}
249
+
250
+ <div
251
+ className={`${CLASSNAME}__sentinel ${CLASSNAME}__sentinel--bottom`}
252
+ ref={setSentinelBottom}
253
+ />
254
+ </div>
255
+
256
+ {(footer || footerChildContent) && (
257
+ <footer
258
+ {...footerChildProps}
259
+ className={classNames(
260
+ `${CLASSNAME}__footer`,
261
+ (forceFooterDivider || hasBottomIntersection) &&
262
+ `${CLASSNAME}__footer--has-divider`,
263
+ footerChildProps?.className,
264
+ )}
265
+ >
266
+ {footer}
267
+ {footerChildContent}
268
+ </footer>
269
+ )}
270
+
271
+ {isLoading && (
272
+ <div className={`${CLASSNAME}__progress-overlay`}>
273
+ <Progress variant={ProgressVariant.circular} />
274
+ </div>
275
+ )}
276
+ </section>
277
+ </ClickAwayProvider>
278
+ </div>
279
+ </ThemeProvider>
280
+ </HeadingLevelProvider>
281
+ </div>
282
+ </Portal>
283
+ ) : null;
285
284
  });
286
285
  Dialog.displayName = COMPONENT_NAME;
287
286
  Dialog.className = CLASSNAME;
@@ -1,7 +1,6 @@
1
1
  import React, { RefObject, useRef, useEffect, AriaAttributes } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
- import { createPortal } from 'react-dom';
5
4
 
6
5
  import { mdiClose } from '@lumx/icons';
7
6
  import { HeadingLevelProvider, IconButton, IconButtonProps } from '@lumx/react';
@@ -17,6 +16,7 @@ import { useCallbackOnEscape } from '@lumx/react/hooks/useCallbackOnEscape';
17
16
  import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibility';
18
17
  import { ThemeProvider } from '@lumx/react/utils/theme/ThemeContext';
19
18
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
19
+ import { Portal } from '@lumx/react/utils/PortalProvider';
20
20
 
21
21
  /**
22
22
  * Defines the props of the component.
@@ -127,52 +127,52 @@ export const Lightbox = forwardRef<LightboxProps, HTMLDivElement>((props, ref) =
127
127
 
128
128
  if (!isOpen && !isVisible) return null;
129
129
 
130
- return createPortal(
131
- /* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */
132
- <div
133
- ref={mergeRefs(ref, wrapperRef)}
134
- {...forwardedProps}
135
- aria-label={ariaLabel}
136
- aria-labelledby={ariaLabelledBy}
137
- aria-modal="true"
138
- role="dialog"
139
- tabIndex={-1}
140
- className={classNames(
141
- className,
142
- handleBasicClasses({
143
- prefix: CLASSNAME,
144
- isHidden: !isOpen,
145
- isShown: isOpen || isVisible,
146
- theme,
147
- }),
148
- )}
149
- style={{ zIndex }}
150
- >
151
- {closeButtonProps && (
152
- <div className={`${CLASSNAME}__close`}>
153
- <IconButton
154
- {...closeButtonProps}
155
- ref={closeButtonRef}
156
- emphasis="low"
157
- hasBackground
158
- icon={mdiClose}
159
- theme="dark"
160
- type="button"
161
- onClick={onClose}
162
- />
163
- </div>
164
- )}
165
- <HeadingLevelProvider level={2}>
166
- <ThemeProvider value={undefined}>
167
- <ClickAwayProvider callback={!preventAutoClose && onClose} childrenRefs={clickAwayRefs}>
168
- <div ref={childrenRef} className={`${CLASSNAME}__wrapper`} role="presentation">
169
- {children}
170
- </div>
171
- </ClickAwayProvider>
172
- </ThemeProvider>
173
- </HeadingLevelProvider>
174
- </div>,
175
- document.body,
130
+ return (
131
+ <Portal>
132
+ <div
133
+ ref={mergeRefs(ref, wrapperRef)}
134
+ {...forwardedProps}
135
+ aria-label={ariaLabel}
136
+ aria-labelledby={ariaLabelledBy}
137
+ aria-modal="true"
138
+ role="dialog"
139
+ tabIndex={-1}
140
+ className={classNames(
141
+ className,
142
+ handleBasicClasses({
143
+ prefix: CLASSNAME,
144
+ isHidden: !isOpen,
145
+ isShown: isOpen || isVisible,
146
+ theme,
147
+ }),
148
+ )}
149
+ style={{ zIndex }}
150
+ >
151
+ {closeButtonProps && (
152
+ <div className={`${CLASSNAME}__close`}>
153
+ <IconButton
154
+ {...closeButtonProps}
155
+ ref={closeButtonRef}
156
+ emphasis="low"
157
+ hasBackground
158
+ icon={mdiClose}
159
+ theme="dark"
160
+ type="button"
161
+ onClick={onClose}
162
+ />
163
+ </div>
164
+ )}
165
+ <HeadingLevelProvider level={2}>
166
+ <ThemeProvider value={undefined}>
167
+ <ClickAwayProvider callback={!preventAutoClose && onClose} childrenRefs={clickAwayRefs}>
168
+ <div ref={childrenRef} className={`${CLASSNAME}__wrapper`} role="presentation">
169
+ {children}
170
+ </div>
171
+ </ClickAwayProvider>
172
+ </ThemeProvider>
173
+ </HeadingLevelProvider>
174
+ </div>
175
+ </Portal>
176
176
  );
177
177
  });
178
178
  Lightbox.displayName = COMPONENT_NAME;
@@ -1,5 +1,4 @@
1
1
  import React, { useRef } from 'react';
2
- import { createPortal } from 'react-dom';
3
2
 
4
3
  import classNames from 'classnames';
5
4
  import isFunction from 'lodash/isFunction';
@@ -13,6 +12,7 @@ import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibili
13
12
  import { mergeRefs } from '@lumx/react/utils/react/mergeRefs';
14
13
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
15
14
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
15
+ import { Portal } from '@lumx/react/utils/PortalProvider';
16
16
 
17
17
  /**
18
18
  * Defines the props of the component.
@@ -98,39 +98,40 @@ export const Notification = forwardRef<NotificationProps, HTMLDivElement>((props
98
98
  return null;
99
99
  }
100
100
 
101
- const notification = (
102
- // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
103
- <div
104
- ref={mergeRefs(ref, rootRef)}
105
- role="alert"
106
- {...forwardedProps}
107
- className={classNames(
108
- className,
109
- handleBasicClasses({
110
- color,
111
- hasAction,
112
- isHidden: !isOpen,
113
- prefix: CLASSNAME,
114
- }),
115
- )}
116
- onClick={onClick}
117
- style={{ ...style, zIndex }}
118
- >
119
- <div className={`${CLASSNAME}__icon`}>
120
- <Icon icon={icon} size={Size.s} />
121
- </div>
122
- <div className={`${CLASSNAME}__content`}>{content}</div>
123
- {hasAction && (
124
- <div className={`${CLASSNAME}__action`}>
125
- <Button emphasis={Emphasis.medium} theme={theme} onClick={handleCallback}>
126
- <span>{actionLabel}</span>
127
- </Button>
101
+ return (
102
+ <Portal enabled={usePortal}>
103
+ {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
104
+ <div
105
+ ref={mergeRefs(ref, rootRef)}
106
+ role="alert"
107
+ {...forwardedProps}
108
+ className={classNames(
109
+ className,
110
+ handleBasicClasses({
111
+ color,
112
+ hasAction,
113
+ isHidden: !isOpen,
114
+ prefix: CLASSNAME,
115
+ }),
116
+ )}
117
+ onClick={onClick}
118
+ style={{ ...style, zIndex }}
119
+ >
120
+ <div className={`${CLASSNAME}__icon`}>
121
+ <Icon icon={icon} size={Size.s} />
128
122
  </div>
129
- )}
130
- </div>
123
+ <div className={`${CLASSNAME}__content`}>{content}</div>
124
+ {hasAction && (
125
+ <div className={`${CLASSNAME}__action`}>
126
+ <Button emphasis={Emphasis.medium} theme={theme} onClick={handleCallback}>
127
+ <span>{actionLabel}</span>
128
+ </Button>
129
+ </div>
130
+ )}
131
+ </div>
132
+ );
133
+ </Portal>
131
134
  );
132
-
133
- return usePortal ? createPortal(notification, document.body) : notification;
134
135
  });
135
136
  Notification.displayName = COMPONENT_NAME;
136
137
  Notification.className = CLASSNAME;
@@ -1,5 +1,4 @@
1
1
  import React, { ReactNode, RefObject, useRef } from 'react';
2
- import { createPortal } from 'react-dom';
3
2
 
4
3
  import classNames from 'classnames';
5
4
 
@@ -15,6 +14,7 @@ import { skipRender } from '@lumx/react/utils/react/skipRender';
15
14
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
16
15
 
17
16
  import { ThemeProvider } from '@lumx/react/utils/theme/ThemeContext';
17
+ import { Portal } from '@lumx/react/utils/PortalProvider';
18
18
  import { useRestoreFocusOnClose } from './useRestoreFocusOnClose';
19
19
  import { usePopoverStyle } from './usePopoverStyle';
20
20
  import { Elevation, FitAnchorWidth, Offset, Placement, POPOVER_ZINDEX } from './constants';
@@ -93,11 +93,6 @@ const DEFAULT_PROPS: Partial<PopoverProps> = {
93
93
  zIndex: POPOVER_ZINDEX,
94
94
  };
95
95
 
96
- /** Method to render the popover inside a portal if usePortal is true */
97
- const renderPopover = (children: ReactNode, usePortal?: boolean): any => {
98
- return usePortal ? createPortal(children, document.body) : children;
99
- };
100
-
101
96
  // Inner component (must be wrapped before export)
102
97
  const _InnerPopover = forwardRef<PopoverProps, HTMLDivElement>((props, ref) => {
103
98
  const {
@@ -154,39 +149,38 @@ const _InnerPopover = forwardRef<PopoverProps, HTMLDivElement>((props, ref) => {
154
149
  const clickAwayRefs = useRef([popoverRef, anchorRef]);
155
150
  const mergedRefs = useMergeRefs<HTMLDivElement>(setPopperElement, ref, popoverRef);
156
151
 
157
- return isOpen
158
- ? renderPopover(
159
- <Component
160
- {...forwardedProps}
161
- ref={mergedRefs}
162
- className={classNames(
163
- className,
164
- handleBasicClasses({
165
- prefix: CLASSNAME,
166
- theme,
167
- elevation: Math.min(elevation || 0, 5),
168
- position,
169
- isInitializing: !styles.popover?.transform,
170
- }),
171
- )}
172
- style={styles.popover}
173
- {...attributes.popper}
174
- >
175
- {unmountSentinel}
176
- <ClickAwayProvider callback={closeOnClickAway && onClose} childrenRefs={clickAwayRefs}>
177
- {hasArrow && (
178
- <div ref={setArrowElement} className={`${CLASSNAME}__arrow`} style={styles.arrow}>
179
- <svg viewBox="0 0 14 14" aria-hidden>
180
- <path d="M8 3.49C7.62 2.82 6.66 2.82 6.27 3.48L.04 14 14.04 14 8 3.49Z" />
181
- </svg>
182
- </div>
183
- )}
184
- <ThemeProvider value={theme}>{children}</ThemeProvider>
185
- </ClickAwayProvider>
186
- </Component>,
187
- usePortal,
188
- )
189
- : null;
152
+ return isOpen ? (
153
+ <Portal enabled={usePortal}>
154
+ <Component
155
+ {...forwardedProps}
156
+ ref={mergedRefs}
157
+ className={classNames(
158
+ className,
159
+ handleBasicClasses({
160
+ prefix: CLASSNAME,
161
+ theme,
162
+ elevation: Math.min(elevation || 0, 5),
163
+ position,
164
+ isInitializing: !styles.popover?.transform,
165
+ }),
166
+ )}
167
+ style={styles.popover}
168
+ {...attributes.popper}
169
+ >
170
+ {unmountSentinel}
171
+ <ClickAwayProvider callback={closeOnClickAway && onClose} childrenRefs={clickAwayRefs}>
172
+ {hasArrow && (
173
+ <div ref={setArrowElement} className={`${CLASSNAME}__arrow`} style={styles.arrow}>
174
+ <svg viewBox="0 0 14 14" aria-hidden>
175
+ <path d="M8 3.49C7.62 2.82 6.66 2.82 6.27 3.48L.04 14 14.04 14 8 3.49Z" />
176
+ </svg>
177
+ </div>
178
+ )}
179
+ <ThemeProvider value={theme}>{children}</ThemeProvider>
180
+ </ClickAwayProvider>
181
+ </Component>
182
+ </Portal>
183
+ ) : null;
190
184
  });
191
185
  _InnerPopover.displayName = COMPONENT_NAME;
192
186
 
@@ -1,6 +1,5 @@
1
1
  /* eslint-disable react-hooks/rules-of-hooks */
2
2
  import React, { ReactNode, useState } from 'react';
3
- import { createPortal } from 'react-dom';
4
3
 
5
4
  import classNames from 'classnames';
6
5
 
@@ -15,6 +14,7 @@ import { usePopper } from '@lumx/react/hooks/usePopper';
15
14
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
16
15
 
17
16
  import { ARIA_LINK_MODES, TOOLTIP_ZINDEX } from '@lumx/react/components/tooltip/constants';
17
+ import { Portal } from '@lumx/react/utils/PortalProvider';
18
18
  import { useInjectTooltipRef } from './useInjectTooltipRef';
19
19
  import { useTooltipOpen } from './useTooltipOpen';
20
20
 
@@ -129,8 +129,8 @@ export const Tooltip = forwardRef<TooltipProps, HTMLDivElement>((props, ref) =>
129
129
  return (
130
130
  <>
131
131
  <TooltipContextProvider>{wrappedChildren}</TooltipContextProvider>
132
- {isMounted &&
133
- createPortal(
132
+ {isMounted && (
133
+ <Portal>
134
134
  <div
135
135
  ref={tooltipRef}
136
136
  {...forwardedProps}
@@ -154,9 +154,9 @@ export const Tooltip = forwardRef<TooltipProps, HTMLDivElement>((props, ref) =>
154
154
  <p key={line}>{line}</p>
155
155
  ))}
156
156
  </div>
157
- </div>,
158
- document.body,
159
- )}
157
+ </div>
158
+ </Portal>
159
+ )}
160
160
  </>
161
161
  );
162
162
  });
@@ -0,0 +1,33 @@
1
+ import { Button } from '@lumx/react';
2
+ import { Portal, PortalProvider } from '@lumx/react/utils';
3
+ import React from 'react';
4
+
5
+ export default {
6
+ title: 'LumX components/PortalProvider',
7
+ component: PortalProvider,
8
+ };
9
+
10
+ const initShadowDOMPortal = () => {
11
+ const div = document.createElement('div');
12
+ document.body.appendChild(div);
13
+ const container = div.attachShadow({ mode: 'closed' });
14
+ const style = document.createElement('style');
15
+ style.innerText = `button { color: red; }`;
16
+ container.appendChild(style);
17
+ return { container, teardown: () => div.remove() };
18
+ };
19
+
20
+ /**
21
+ * Demonstrate how to customize portals to render into a custom shadow root
22
+ */
23
+ export const RenderInShadowDOM = {
24
+ args: { enabled: true },
25
+ render: ({ enabled }) => (
26
+ <PortalProvider value={initShadowDOMPortal}>
27
+ <Portal enabled={enabled}>
28
+ <Button>My button {!enabled && 'not'} in a shadow DOM portal</Button>
29
+ </Portal>
30
+ </PortalProvider>
31
+ ),
32
+ parameters: { chromatic: { disable: true } },
33
+ };
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ type Container = DocumentFragment | Element;
5
+ export type PortalInit = () => { container: Container; teardown?: () => void };
6
+
7
+ const PortalContext = React.createContext<PortalInit>(() => ({ container: document.body }));
8
+
9
+ /**
10
+ * Customize where <Portal> wrapped elements render (tooltip, popover, dialog, etc.)
11
+ */
12
+ export const PortalProvider: React.FC<{ children?: React.ReactNode; value: PortalInit }> = PortalContext.Provider;
13
+
14
+ /**
15
+ * Render children in a portal outside the current DOM position
16
+ * (defaults to `document.body` but can be customized with the PortalContextProvider)
17
+ */
18
+ export const Portal: React.FC<{ enabled?: boolean; children: React.ReactNode }> = ({ children, enabled = true }) => {
19
+ const init = React.useContext(PortalContext);
20
+ const context = React.useMemo(
21
+ () => {
22
+ if (!enabled) return undefined;
23
+ return init();
24
+ },
25
+ // Only update on 'enabled'
26
+ // eslint-disable-next-line react-hooks/exhaustive-deps
27
+ [enabled],
28
+ );
29
+
30
+ React.useLayoutEffect(() => {
31
+ return context?.teardown;
32
+ }, [context?.teardown, enabled]);
33
+
34
+ if (!enabled) return <>{children}</>;
35
+ return createPortal(children, context?.container as Container);
36
+ };
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from './ClickAwayProvider';
6
+ export * from './PortalProvider';