@clayui/tooltip 3.55.0 → 3.65.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/lib/Tooltip.d.ts CHANGED
@@ -1,7 +1,17 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ */
1
5
  import React from 'react';
2
6
  export declare const ALIGN_POSITIONS: readonly ["top", "top-left", "top-right", "bottom", "bottom-left", "bottom-right", "left", "right"];
3
7
  interface IProps extends React.HTMLAttributes<HTMLDivElement> {
8
+ /**
9
+ * Position in which the tooltip will be aligned to the element.
10
+ */
4
11
  alignPosition?: typeof ALIGN_POSITIONS[number];
12
+ /**
13
+ * Flag to indicate if tooltip is displayed.
14
+ */
5
15
  show?: boolean;
6
16
  }
7
17
  declare const ClayTooltip: React.ForwardRefExoticComponent<IProps & React.RefAttributes<HTMLElement>>;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ */
1
5
  import { IPortalBaseProps } from '@clayui/shared';
2
6
  import React from 'react';
3
7
  declare type TContentRenderer = (props: {
@@ -5,9 +9,21 @@ declare type TContentRenderer = (props: {
5
9
  title: string;
6
10
  }) => React.ReactElement | React.ReactNode;
7
11
  interface IPropsBase {
12
+ /**
13
+ * Flag to indicate if tooltip should automatically align based on the window
14
+ */
8
15
  autoAlign?: boolean;
16
+ /**
17
+ * Props to add to the <ClayPortal/>.
18
+ */
9
19
  containerProps?: IPortalBaseProps;
20
+ /**
21
+ * Custom function for rendering the contents of the tooltip
22
+ */
10
23
  contentRenderer?: TContentRenderer;
24
+ /**
25
+ * Delay in miliseconds before showing tooltip
26
+ */
11
27
  delay?: number;
12
28
  }
13
29
  interface IPropsWithChildren extends IPropsBase {
@@ -16,7 +32,12 @@ interface IPropsWithChildren extends IPropsBase {
16
32
  }
17
33
  interface IPropsWithScope extends IPropsBase {
18
34
  children?: never;
35
+ /**
36
+ * CSS selector to scope provider to. All titles within this scope will be
37
+ * rendered in the tooltip. Titles outside of this scope will be styled
38
+ * as with the default browser.
39
+ */
19
40
  scope: string;
20
41
  }
21
- declare const TooltipProvider: React.FunctionComponent<IPropsWithChildren | IPropsWithScope>;
42
+ declare const TooltipProvider: ({ autoAlign, children, containerProps, contentRenderer, delay, scope, }: IPropsWithChildren | IPropsWithScope) => JSX.Element;
22
43
  export default TooltipProvider;
@@ -150,25 +150,21 @@ var TooltipProvider = function TooltipProvider(_ref2) {
150
150
  delay = _ref2$delay === void 0 ? 600 : _ref2$delay,
151
151
  scope = _ref2.scope;
152
152
 
153
- var _React$useReducer = _react.default.useReducer(reducer, initialState),
154
- _React$useReducer2 = _slicedToArray(_React$useReducer, 2),
155
- _React$useReducer2$ = _React$useReducer2[0],
156
- align = _React$useReducer2$.align,
157
- _React$useReducer2$$m = _React$useReducer2$.message,
158
- message = _React$useReducer2$$m === void 0 ? '' : _React$useReducer2$$m,
159
- setAsHTML = _React$useReducer2$.setAsHTML,
160
- show = _React$useReducer2$.show,
161
- dispatch = _React$useReducer2[1]; // Using `any` type since TS incorrectly infers setTimeout to be from NodeJS
162
-
163
-
164
- var timeoutIdRef = _react.default.useRef();
165
-
166
- var targetRef = _react.default.useRef(null);
167
-
168
- var titleNodeRef = _react.default.useRef(null);
169
-
170
- var tooltipRef = _react.default.useRef(null);
171
-
153
+ var _useReducer = (0, _react.useReducer)(reducer, initialState),
154
+ _useReducer2 = _slicedToArray(_useReducer, 2),
155
+ _useReducer2$ = _useReducer2[0],
156
+ align = _useReducer2$.align,
157
+ _useReducer2$$message = _useReducer2$.message,
158
+ message = _useReducer2$$message === void 0 ? '' : _useReducer2$$message,
159
+ setAsHTML = _useReducer2$.setAsHTML,
160
+ show = _useReducer2$.show,
161
+ dispatch = _useReducer2[1]; // Using `any` type since TS incorrectly infers setTimeout to be from NodeJS
162
+
163
+
164
+ var timeoutIdRef = (0, _react.useRef)();
165
+ var targetRef = (0, _react.useRef)(null);
166
+ var titleNodeRef = (0, _react.useRef)(null);
167
+ var tooltipRef = (0, _react.useRef)(null);
172
168
  var saveTitle = (0, _react.useCallback)(function (element) {
173
169
  titleNodeRef.current = element;
174
170
  var title = element.getAttribute('title');
@@ -206,7 +202,13 @@ var TooltipProvider = function TooltipProvider(_ref2) {
206
202
  titleNodeRef.current = null;
207
203
  }
208
204
  }, []);
209
- var handleHide = (0, _react.useCallback)(function () {
205
+ var handleHide = (0, _react.useCallback)(function (event) {
206
+ var _tooltipRef$current, _targetRef$current;
207
+
208
+ if (event && ((_tooltipRef$current = tooltipRef.current) !== null && _tooltipRef$current !== void 0 && _tooltipRef$current.contains(event.relatedTarget) || (_targetRef$current = targetRef.current) !== null && _targetRef$current !== void 0 && _targetRef$current.contains(event.relatedTarget))) {
209
+ return;
210
+ }
211
+
210
212
  dispatch({
211
213
  type: 'hide'
212
214
  });
@@ -220,11 +222,11 @@ var TooltipProvider = function TooltipProvider(_ref2) {
220
222
  }, []);
221
223
  var handleShow = (0, _react.useCallback)(function (_ref3) {
222
224
  var target = _ref3.target;
223
- targetRef.current = target;
224
225
  var hasTitle = target && (target.hasAttribute('title') || target.hasAttribute('data-title'));
225
226
  var titleNode = hasTitle ? target : closestAncestor(target, '[title], [data-title]');
226
227
 
227
228
  if (titleNode) {
229
+ targetRef.current = target;
228
230
  target.addEventListener('click', handleHide);
229
231
  var title = titleNode.getAttribute('title') || titleNode.getAttribute('data-title') || '';
230
232
  saveTitle(titleNode);
@@ -233,6 +235,7 @@ var TooltipProvider = function TooltipProvider(_ref2) {
233
235
 
234
236
  var _setAsHTML = !!titleNode.getAttribute('data-title-set-as-html');
235
237
 
238
+ clearTimeout(timeoutIdRef.current);
236
239
  timeoutIdRef.current = setTimeout(function () {
237
240
  dispatch({
238
241
  align: newAlign || align,
@@ -243,8 +246,7 @@ var TooltipProvider = function TooltipProvider(_ref2) {
243
246
  }, customDelay ? Number(customDelay) : delay);
244
247
  }
245
248
  }, []);
246
-
247
- _react.default.useEffect(function () {
249
+ (0, _react.useEffect)(function () {
248
250
  var handleEsc = function handleEsc(event) {
249
251
  if (show && event.key === _shared.Keys.Esc) {
250
252
  event.stopImmediatePropagation();
@@ -257,14 +259,13 @@ var TooltipProvider = function TooltipProvider(_ref2) {
257
259
  return document.removeEventListener('keyup', handleEsc, true);
258
260
  };
259
261
  }, [show]);
260
-
261
- _react.default.useEffect(function () {
262
+ (0, _react.useEffect)(function () {
262
263
  if (scope) {
263
264
  var disposeShowEvents = TRIGGER_SHOW_EVENTS.map(function (eventName) {
264
265
  return (0, _shared.delegate)(document.body, eventName, scope, handleShow);
265
266
  });
266
267
  var disposeHideEvents = TRIGGER_HIDE_EVENTS.map(function (eventName) {
267
- return (0, _shared.delegate)(document.body, eventName, scope, handleHide);
268
+ return (0, _shared.delegate)(document.body, eventName, "".concat(scope, ", .tooltip"), handleHide);
268
269
  });
269
270
  return function () {
270
271
  disposeShowEvents.forEach(function (_ref4) {
@@ -278,8 +279,7 @@ var TooltipProvider = function TooltipProvider(_ref2) {
278
279
  };
279
280
  }
280
281
  }, []);
281
-
282
- _react.default.useEffect(function () {
282
+ (0, _react.useEffect)(function () {
283
283
  if (titleNodeRef.current && tooltipRef.current) {
284
284
  var points = ALIGNMENTS_MAP[align || 'top'];
285
285
  var alignment = (0, _shared.doAlign)({
@@ -307,7 +307,6 @@ var TooltipProvider = function TooltipProvider(_ref2) {
307
307
  }
308
308
  }
309
309
  }, [align, show]);
310
-
311
310
  "production" !== "production" ? (0, _warning.default)(typeof children === 'undefined' && typeof scope !== 'undefined' || typeof scope === 'undefined' && typeof children !== 'undefined', '<TooltipProvider />: You must use at least one of the following props: `children` or `scope`.') : void 0;
312
311
  "production" !== "production" ? (0, _warning.default)(typeof children !== 'undefined' || typeof scope !== 'undefined', '<TooltipProvider />: If you want to use `scope`, use <TooltipProvider /> as a singleton and do not pass `children`.') : void 0;
313
312
  "production" !== "production" ? (0, _warning.default)((children === null || children === void 0 ? void 0 : children.type) !== _react.default.Fragment, '<TooltipProvider />: React Fragment is not allowed as a child to TooltipProvider. Child must be a single HTML element that accepts `onMouseOver` and `onMouseOut`.') : void 0;
@@ -315,7 +314,8 @@ var TooltipProvider = function TooltipProvider(_ref2) {
315
314
  targetNode: targetRef.current,
316
315
  title: message
317
316
  });
318
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, show && /*#__PURE__*/_react.default.createElement(_shared.ClayPortal, containerProps, /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
317
+
318
+ var tooltip = show && /*#__PURE__*/_react.default.createElement(_shared.ClayPortal, containerProps, /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
319
319
  alignPosition: align,
320
320
  ref: tooltipRef,
321
321
  show: true
@@ -323,7 +323,10 @@ var TooltipProvider = function TooltipProvider(_ref2) {
323
323
  dangerouslySetInnerHTML: {
324
324
  __html: titleContent
325
325
  }
326
- }) : titleContent)), scope ? children : children && /*#__PURE__*/_react.default.cloneElement(children, _objectSpread(_objectSpread({}, children.props), {}, {
326
+ }) : titleContent));
327
+
328
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, scope ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, tooltip, children) : children && /*#__PURE__*/_react.default.cloneElement(children, _objectSpread(_objectSpread({}, children.props), {}, {
329
+ children: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children.props.children, tooltip),
327
330
  onMouseOut: handleHide,
328
331
  onMouseOver: handleShow
329
332
  })));
package/lib/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ */
1
5
  import ClayTooltip from './Tooltip';
2
6
  import ClayTooltipProvider from './TooltipProvider';
3
7
  export { ClayTooltipProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clayui/tooltip",
3
- "version": "3.55.0",
3
+ "version": "3.65.0",
4
4
  "description": "ClayTooltip component",
5
5
  "license": "BSD-3-Clause",
6
6
  "repository": "https://github.com/liferay/clay",
@@ -17,8 +17,8 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "cross-env NODE_ENV=production babel src --root-mode upward --out-dir lib --extensions .ts,.tsx",
20
- "build:types": "cross-env NODE_ENV=production tsc --project ./tsconfig.declarations.json",
21
- "prepublishOnly": "yarn build && yarn build:types",
20
+ "buildTypes": "cross-env NODE_ENV=production tsc --project ./tsconfig.declarations.json",
21
+ "prepublishOnly": "yarn build && yarn buildTypes",
22
22
  "test": "jest --config ../../jest.config.js"
23
23
  },
24
24
  "keywords": [
@@ -26,7 +26,7 @@
26
26
  "react"
27
27
  ],
28
28
  "dependencies": {
29
- "@clayui/shared": "^3.55.0",
29
+ "@clayui/shared": "^3.65.0",
30
30
  "classnames": "^2.2.6",
31
31
  "warning": "^4.0.3"
32
32
  },
@@ -38,5 +38,5 @@
38
38
  "browserslist": [
39
39
  "extends browserslist-config-clay"
40
40
  ],
41
- "gitHead": "5310f4650d453a3659b0bc73a5f098732168c9c1"
41
+ "gitHead": "603f05c8d617d98e29f0456b5d6618f9a95ce740"
42
42
  }
@@ -10,7 +10,7 @@ import {
10
10
  delegate,
11
11
  doAlign,
12
12
  } from '@clayui/shared';
13
- import React, {useCallback} from 'react';
13
+ import React, {useCallback, useEffect, useReducer, useRef} from 'react';
14
14
  import warning from 'warning';
15
15
 
16
16
  import ClayTooltip from './Tooltip';
@@ -183,26 +183,24 @@ interface IPropsWithScope extends IPropsBase {
183
183
  scope: string;
184
184
  }
185
185
 
186
- const TooltipProvider: React.FunctionComponent<
187
- IPropsWithChildren | IPropsWithScope
188
- > = ({
186
+ const TooltipProvider = ({
189
187
  autoAlign = true,
190
188
  children,
191
189
  containerProps = {},
192
190
  contentRenderer = (props) => props.title,
193
191
  delay = 600,
194
192
  scope,
195
- }) => {
196
- const [{align, message = '', setAsHTML, show}, dispatch] = React.useReducer(
193
+ }: IPropsWithChildren | IPropsWithScope) => {
194
+ const [{align, message = '', setAsHTML, show}, dispatch] = useReducer(
197
195
  reducer,
198
196
  initialState
199
197
  );
200
198
 
201
199
  // Using `any` type since TS incorrectly infers setTimeout to be from NodeJS
202
- const timeoutIdRef = React.useRef<any>();
203
- const targetRef = React.useRef<HTMLElement | null>(null);
204
- const titleNodeRef = React.useRef<HTMLElement | null>(null);
205
- const tooltipRef = React.useRef<HTMLElement | null>(null);
200
+ const timeoutIdRef = useRef<any>();
201
+ const targetRef = useRef<HTMLElement | null>(null);
202
+ const titleNodeRef = useRef<HTMLElement | null>(null);
203
+ const tooltipRef = useRef<HTMLElement | null>(null);
206
204
 
207
205
  const saveTitle = useCallback((element: HTMLElement) => {
208
206
  titleNodeRef.current = element;
@@ -247,7 +245,15 @@ const TooltipProvider: React.FunctionComponent<
247
245
  }
248
246
  }, []);
249
247
 
250
- const handleHide = useCallback(() => {
248
+ const handleHide = useCallback((event?: any) => {
249
+ if (
250
+ event &&
251
+ (tooltipRef.current?.contains(event.relatedTarget) ||
252
+ targetRef.current?.contains(event.relatedTarget))
253
+ ) {
254
+ return;
255
+ }
256
+
251
257
  dispatch({type: 'hide'});
252
258
 
253
259
  clearTimeout(timeoutIdRef.current);
@@ -262,8 +268,6 @@ const TooltipProvider: React.FunctionComponent<
262
268
  }, []);
263
269
 
264
270
  const handleShow = useCallback(({target}: {target: HTMLElement}) => {
265
- targetRef.current = target;
266
-
267
271
  const hasTitle =
268
272
  target &&
269
273
  (target.hasAttribute('title') || target.hasAttribute('data-title'));
@@ -273,6 +277,8 @@ const TooltipProvider: React.FunctionComponent<
273
277
  : closestAncestor(target, '[title], [data-title]');
274
278
 
275
279
  if (titleNode) {
280
+ targetRef.current = target;
281
+
276
282
  target.addEventListener('click', handleHide);
277
283
 
278
284
  const title =
@@ -290,6 +296,8 @@ const TooltipProvider: React.FunctionComponent<
290
296
  'data-title-set-as-html'
291
297
  );
292
298
 
299
+ clearTimeout(timeoutIdRef.current);
300
+
293
301
  timeoutIdRef.current = setTimeout(
294
302
  () => {
295
303
  dispatch({
@@ -304,7 +312,7 @@ const TooltipProvider: React.FunctionComponent<
304
312
  }
305
313
  }, []);
306
314
 
307
- React.useEffect(() => {
315
+ useEffect(() => {
308
316
  const handleEsc = (event: KeyboardEvent) => {
309
317
  if (show && event.key === Keys.Esc) {
310
318
  event.stopImmediatePropagation();
@@ -318,13 +326,18 @@ const TooltipProvider: React.FunctionComponent<
318
326
  return () => document.removeEventListener('keyup', handleEsc, true);
319
327
  }, [show]);
320
328
 
321
- React.useEffect(() => {
329
+ useEffect(() => {
322
330
  if (scope) {
323
331
  const disposeShowEvents = TRIGGER_SHOW_EVENTS.map((eventName) => {
324
332
  return delegate(document.body, eventName, scope, handleShow);
325
333
  });
326
334
  const disposeHideEvents = TRIGGER_HIDE_EVENTS.map((eventName) => {
327
- return delegate(document.body, eventName, scope, handleHide);
335
+ return delegate(
336
+ document.body,
337
+ eventName,
338
+ `${scope}, .tooltip`,
339
+ handleHide
340
+ );
328
341
  });
329
342
 
330
343
  return () => {
@@ -334,7 +347,7 @@ const TooltipProvider: React.FunctionComponent<
334
347
  }
335
348
  }, []);
336
349
 
337
- React.useEffect(() => {
350
+ useEffect(() => {
338
351
  if (
339
352
  titleNodeRef.current &&
340
353
  (tooltipRef as React.RefObject<HTMLDivElement>).current
@@ -393,32 +406,43 @@ const TooltipProvider: React.FunctionComponent<
393
406
  title: message,
394
407
  });
395
408
 
409
+ const tooltip = show && (
410
+ <ClayPortal {...containerProps}>
411
+ <ClayTooltip alignPosition={align} ref={tooltipRef} show>
412
+ {setAsHTML && typeof titleContent === 'string' ? (
413
+ <span
414
+ dangerouslySetInnerHTML={{
415
+ __html: titleContent,
416
+ }}
417
+ />
418
+ ) : (
419
+ titleContent
420
+ )}
421
+ </ClayTooltip>
422
+ </ClayPortal>
423
+ );
424
+
396
425
  return (
397
426
  <>
398
- {show && (
399
- <ClayPortal {...containerProps}>
400
- <ClayTooltip alignPosition={align} ref={tooltipRef} show>
401
- {setAsHTML && typeof titleContent === 'string' ? (
402
- <span
403
- dangerouslySetInnerHTML={{
404
- __html: titleContent,
405
- }}
406
- />
407
- ) : (
408
- titleContent
409
- )}
410
- </ClayTooltip>
411
- </ClayPortal>
427
+ {scope ? (
428
+ <>
429
+ {tooltip}
430
+ {children}
431
+ </>
432
+ ) : (
433
+ children &&
434
+ React.cloneElement(children, {
435
+ ...children.props,
436
+ children: (
437
+ <>
438
+ {children.props.children}
439
+ {tooltip}
440
+ </>
441
+ ),
442
+ onMouseOut: handleHide,
443
+ onMouseOver: handleShow,
444
+ })
412
445
  )}
413
-
414
- {scope
415
- ? children
416
- : children &&
417
- React.cloneElement(children, {
418
- ...children.props,
419
- onMouseOut: handleHide,
420
- onMouseOver: handleShow,
421
- })}
422
446
  </>
423
447
  );
424
448
  };