@bpmn-io/properties-panel 3.26.1 → 3.26.2

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/dist/index.esm.js CHANGED
@@ -283,7 +283,7 @@ function TooltipWrapper(props) {
283
283
  return jsx(Tooltip, {
284
284
  ...props,
285
285
  value: value,
286
- forId: prefixId$9(forId)
286
+ forId: `bio-properties-panel-${forId}`
287
287
  });
288
288
  }
289
289
  function Tooltip(props) {
@@ -294,71 +294,52 @@ function Tooltip(props) {
294
294
  direction = 'right',
295
295
  position
296
296
  } = props;
297
- const [visible, setShow] = useState(false);
298
- const [focusedViaKeyboard, setFocusedViaKeyboard] = useState(false);
297
+ const [visible, setVisible] = useState(false);
298
+
299
+ // Tooltip will be shown after SHOW_DELAY ms from hovering over the source element.
300
+ const SHOW_DELAY = 200;
299
301
  let timeout = null;
300
302
  const wrapperRef = useRef(null);
301
303
  const tooltipRef = useRef(null);
302
- const showTooltip = async event => {
303
- const show = () => setShow(true);
304
- if (!visible && !timeout) {
305
- if (event instanceof MouseEvent) {
306
- timeout = setTimeout(show, 200);
307
- } else {
308
- show();
309
- setFocusedViaKeyboard(true);
310
- }
304
+ const show = (_, delay) => {
305
+ if (visible) return;
306
+ if (delay) {
307
+ timeout = setTimeout(() => {
308
+ setVisible(true);
309
+ }, SHOW_DELAY);
310
+ } else {
311
+ setVisible(true);
311
312
  }
312
313
  };
313
- const hideTooltip = () => {
314
- setShow(false);
315
- setFocusedViaKeyboard(false);
314
+ const hide = () => {
315
+ clearTimeout(timeout);
316
+ setVisible(false);
316
317
  };
317
- const hideTooltipViaEscape = e => {
318
- e.code === 'Escape' && hideTooltip();
319
- };
320
- const isTooltipHovered = ({
321
- x,
322
- y
318
+ const handleMouseLeave = ({
319
+ relatedTarget
323
320
  }) => {
324
- const tooltip = tooltipRef.current;
325
- const wrapper = wrapperRef.current;
326
- return tooltip && (inBounds(x, y, wrapper.getBoundingClientRect()) || inBounds(x, y, tooltip.getBoundingClientRect()));
321
+ // Don't hide the tooltip when moving mouse between the wrapper and the tooltip.
322
+ if (relatedTarget === wrapperRef.current || relatedTarget === tooltipRef.current || relatedTarget?.parentElement === tooltipRef.current) {
323
+ return;
324
+ }
325
+ hide();
327
326
  };
328
- useEffect(() => {
327
+ const handleFocusOut = e => {
329
328
  const {
330
- current
331
- } = wrapperRef;
332
- if (!current) {
329
+ target
330
+ } = e;
331
+
332
+ // Don't hide the tooltip if the wrapper or the tooltip itself is clicked.
333
+ const isHovered = target.matches(':hover') || tooltipRef.current?.matches(':hover');
334
+ if (target === wrapperRef.current && isHovered) {
335
+ e.stopPropagation();
333
336
  return;
334
337
  }
335
- const hideHoveredTooltip = e => {
336
- const isFocused = document.activeElement === wrapperRef.current || document.activeElement.closest('.bio-properties-panel-tooltip');
337
- if (visible && !isTooltipHovered({
338
- x: e.x,
339
- y: e.y
340
- }) && !(isFocused && focusedViaKeyboard)) {
341
- hideTooltip();
342
- }
343
- };
344
- const hideFocusedTooltip = e => {
345
- const {
346
- relatedTarget
347
- } = e;
348
- const isTooltipChild = el => !!el.closest('.bio-properties-panel-tooltip');
349
- if (visible && !isHovered(wrapperRef.current) && relatedTarget && !isTooltipChild(relatedTarget)) {
350
- hideTooltip();
351
- }
352
- };
353
- document.addEventListener('wheel', hideHoveredTooltip);
354
- document.addEventListener('focusout', hideFocusedTooltip);
355
- document.addEventListener('mousemove', hideHoveredTooltip);
356
- return () => {
357
- document.removeEventListener('wheel', hideHoveredTooltip);
358
- document.removeEventListener('mousemove', hideHoveredTooltip);
359
- document.removeEventListener('focusout', hideFocusedTooltip);
360
- };
361
- }, [wrapperRef.current, visible, focusedViaKeyboard]);
338
+ hide();
339
+ };
340
+ const hideTooltipViaEscape = e => {
341
+ e.code === 'Escape' && hide();
342
+ };
362
343
  const renderTooltip = () => {
363
344
  return jsxs("div", {
364
345
  class: `bio-properties-panel-tooltip ${direction}`,
@@ -368,6 +349,7 @@ function Tooltip(props) {
368
349
  style: position || getTooltipPosition(wrapperRef.current),
369
350
  ref: tooltipRef,
370
351
  onClick: e => e.stopPropagation(),
352
+ onMouseLeave: handleMouseLeave,
371
353
  children: [jsx("div", {
372
354
  class: "bio-properties-panel-tooltip-content",
373
355
  children: value
@@ -380,39 +362,23 @@ function Tooltip(props) {
380
362
  class: "bio-properties-panel-tooltip-wrapper",
381
363
  tabIndex: "0",
382
364
  ref: wrapperRef,
383
- onMouseEnter: showTooltip,
384
- onMouseLeave: () => {
385
- clearTimeout(timeout);
386
- timeout = null;
387
- },
388
- onFocus: showTooltip,
365
+ onMouseEnter: e => show(e, true),
366
+ onMouseLeave: handleMouseLeave,
367
+ onFocus: show,
368
+ onBlur: handleFocusOut,
389
369
  onKeyDown: hideTooltipViaEscape,
390
370
  children: [props.children, visible ? parent ? createPortal(renderTooltip(), parent.current) : renderTooltip() : null]
391
371
  });
392
372
  }
393
373
 
394
374
  // helper
395
- function inBounds(x, y, bounds) {
396
- const {
397
- top,
398
- right,
399
- bottom,
400
- left
401
- } = bounds;
402
- return x >= left && x <= right && y >= top && y <= bottom;
403
- }
375
+
404
376
  function getTooltipPosition(refElement) {
405
377
  const refPosition = refElement.getBoundingClientRect();
406
378
  const right = `calc(100% - ${refPosition.x}px)`;
407
379
  const top = `${refPosition.top - 10}px`;
408
380
  return `right: ${right}; top: ${top};`;
409
381
  }
410
- function isHovered(element) {
411
- return element.matches(':hover');
412
- }
413
- function prefixId$9(id) {
414
- return `bio-properties-panel-${id}`;
415
- }
416
382
 
417
383
  /**
418
384
  * Accesses the global DescriptionContext and returns a description for a given id and element.