@itwin/itwinui-react 2.12.17 → 2.12.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.12.18
4
+
5
+ ### Patch Changes
6
+
7
+ - a68561cf: Fixed an issue where Dialog content was appearing blurred on Windows.
8
+
3
9
  ## 2.12.17
4
10
 
5
11
  ### Patch Changes
@@ -64,7 +64,7 @@ exports.DialogMain = React.forwardRef((props, ref) => {
64
64
  (0, index_js_1.useTheme)();
65
65
  const [style, setStyle] = React.useState();
66
66
  const dialogRef = React.useRef(null);
67
- const refs = (0, index_js_1.useMergedRefs)(dialogRef, ref);
67
+ const [dialogElement, setDialogElement] = React.useState();
68
68
  const hasBeenResized = React.useRef(false);
69
69
  const previousFocusedElement = React.useRef();
70
70
  const originalBodyOverflow = React.useRef('');
@@ -118,7 +118,7 @@ exports.DialogMain = React.forwardRef((props, ref) => {
118
118
  return;
119
119
  }
120
120
  const rect = (_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
121
- const [translateX, translateY] = (0, index_js_1.getTranslateValues)(dialogRef.current);
121
+ const [translateX, translateY] = (0, index_js_1.getTranslateValuesFromElement)(dialogRef.current);
122
122
  setStyle((oldStyle) => {
123
123
  var _a, _b;
124
124
  return ({
@@ -137,13 +137,17 @@ exports.DialogMain = React.forwardRef((props, ref) => {
137
137
  ...newStyle,
138
138
  }));
139
139
  }, []);
140
+ const roundedTransform = useRoundedTransform({
141
+ element: dialogElement,
142
+ transform,
143
+ });
140
144
  const content = (React.createElement("div", { className: (0, classnames_1.default)('iui-dialog', {
141
145
  'iui-dialog-default': styleType === 'default',
142
146
  'iui-dialog-full-page': styleType === 'fullPage',
143
147
  'iui-dialog-visible': isOpen,
144
148
  'iui-dialog-draggable': isDraggable,
145
- }, className), role: 'dialog', ref: refs, onKeyDown: handleKeyDown, tabIndex: -1, "data-iui-placement": placement, style: {
146
- transform,
149
+ }, className), role: 'dialog', ref: (0, index_js_1.useMergedRefs)(dialogRef, ref, setDialogElement), onKeyDown: handleKeyDown, tabIndex: -1, "data-iui-placement": placement, style: {
150
+ transform: roundedTransform,
147
151
  ...style,
148
152
  ...propStyle,
149
153
  }, ...rest },
@@ -176,3 +180,20 @@ exports.DialogMain = React.forwardRef((props, ref) => {
176
180
  !trapFocus && content)));
177
181
  });
178
182
  exports.default = exports.DialogMain;
183
+ // ----------------------------------------------------------------------------
184
+ /**
185
+ * Rounds off an element's transform value based on the device's pixel grid, to avoid blurring.
186
+ */
187
+ const useRoundedTransform = ({ element, transform, }) => {
188
+ const [roundedStyles, setRoundedStyles] = React.useState(transform);
189
+ (0, index_js_1.useIsomorphicLayoutEffect)(() => {
190
+ if (!element || typeof DOMMatrix === 'undefined') {
191
+ return;
192
+ }
193
+ const [x, y] = transform
194
+ ? (0, index_js_1.getTranslateValues)(transform)
195
+ : (0, index_js_1.getTranslateValuesFromElement)(element);
196
+ setRoundedStyles(`translate(${(0, index_js_1.roundByDPR)(x)}px, ${(0, index_js_1.roundByDPR)(y)}px)`);
197
+ }, [element, transform]);
198
+ return roundedStyles;
199
+ };
@@ -51,7 +51,7 @@ const Resizer = (props) => {
51
51
  }
52
52
  const initialPointerX = event.clientX;
53
53
  const initialPointerY = event.clientY;
54
- const [initialTranslateX, initialTranslateY] = (0, index_js_1.getTranslateValues)(elementRef.current);
54
+ const [initialTranslateX, initialTranslateY] = (0, index_js_1.getTranslateValuesFromElement)(elementRef.current);
55
55
  const { width: initialWidth, height: initialHeight } = elementRef.current.getBoundingClientRect();
56
56
  let width = `${initialWidth}px`;
57
57
  let height = `${initialHeight}px`;
@@ -27,4 +27,5 @@ export declare const mergeEventHandlers: <E extends import("react").SyntheticEve
27
27
  * @param element HTML element you want to get translate value of
28
28
  * @returns Translate values in pixels in an array `[translateX, translateY]`
29
29
  */
30
- export declare const getTranslateValues: (element: HTMLElement | null | undefined) => number[];
30
+ export declare const getTranslateValuesFromElement: (element: HTMLElement | null | undefined) => number[];
31
+ export declare const getTranslateValues: (transformValue: string) => number[];
@@ -4,7 +4,7 @@
4
4
  * See LICENSE.md in the project root for license terms and full copyright notice.
5
5
  *--------------------------------------------------------------------------------------------*/
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.getTranslateValues = exports.mergeEventHandlers = exports.getWindow = exports.getDocument = exports.getContainer = void 0;
7
+ exports.getTranslateValues = exports.getTranslateValuesFromElement = exports.mergeEventHandlers = exports.getWindow = exports.getDocument = exports.getContainer = void 0;
8
8
  /**
9
9
  * Get the container as a child of body, or create one if it doesn't exist.
10
10
  * Mostly used for dynamic components like Modal or Toast.
@@ -58,11 +58,15 @@ exports.mergeEventHandlers = mergeEventHandlers;
58
58
  * @param element HTML element you want to get translate value of
59
59
  * @returns Translate values in pixels in an array `[translateX, translateY]`
60
60
  */
61
- const getTranslateValues = (element) => {
61
+ const getTranslateValuesFromElement = (element) => {
62
62
  if (!element) {
63
63
  return [];
64
64
  }
65
65
  const transformValue = getComputedStyle(element).getPropertyValue('transform');
66
+ return (0, exports.getTranslateValues)(transformValue);
67
+ };
68
+ exports.getTranslateValuesFromElement = getTranslateValuesFromElement;
69
+ const getTranslateValues = (transformValue) => {
66
70
  const matrix = new DOMMatrix(transformValue);
67
71
  return [matrix.m41, matrix.m42];
68
72
  };
@@ -6,3 +6,10 @@ export declare const getBoundedValue: (val: number, min: number, max: number) =>
6
6
  * Returns a random value of a given length containing `A-Za-z0-9_-` symbols.
7
7
  */
8
8
  export declare const getRandomValue: (length?: number) => string;
9
+ /**
10
+ * Rounds a pixel value based on the device's pixel ratio. This ensures that values can be
11
+ * placed evenly on the device’s pixel grid, avoiding any blurring.
12
+ *
13
+ * @see https://floating-ui.com/docs/misc#subpixel-and-accelerated-positioning
14
+ */
15
+ export declare const roundByDPR: (value: number) => number;
@@ -4,7 +4,7 @@
4
4
  * See LICENSE.md in the project root for license terms and full copyright notice.
5
5
  *--------------------------------------------------------------------------------------------*/
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.getRandomValue = exports.getBoundedValue = void 0;
7
+ exports.roundByDPR = exports.getRandomValue = exports.getBoundedValue = void 0;
8
8
  /**
9
9
  * Return input value bounded by specified range.
10
10
  */
@@ -24,3 +24,14 @@ const getRandomValue = (length = 21) => {
24
24
  return id;
25
25
  };
26
26
  exports.getRandomValue = getRandomValue;
27
+ /**
28
+ * Rounds a pixel value based on the device's pixel ratio. This ensures that values can be
29
+ * placed evenly on the device’s pixel grid, avoiding any blurring.
30
+ *
31
+ * @see https://floating-ui.com/docs/misc#subpixel-and-accelerated-positioning
32
+ */
33
+ const roundByDPR = (value) => {
34
+ const dpr = window.devicePixelRatio || 1;
35
+ return Math.round(value * dpr) / dpr;
36
+ };
37
+ exports.roundByDPR = roundByDPR;
@@ -62,7 +62,7 @@ const useDragAndDrop = (elementRef, containerRef, enabled = true) => {
62
62
  return;
63
63
  }
64
64
  const { top, right, bottom, left } = (_a = elementRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
65
- let [newTranslateX, newTranslateY] = (0, index_js_1.getTranslateValues)(elementRef.current);
65
+ let [newTranslateX, newTranslateY] = (0, index_js_1.getTranslateValuesFromElement)(elementRef.current);
66
66
  containerRectRef.current = getContainerRect(containerRef);
67
67
  if (bottom > containerRectRef.current.bottom) {
68
68
  newTranslateY -= bottom - containerRectRef.current.bottom;
@@ -108,7 +108,7 @@ const useDragAndDrop = (elementRef, containerRef, enabled = true) => {
108
108
  if (!elementRef.current || e.button !== 0 || !enabled) {
109
109
  return;
110
110
  }
111
- const [x, y] = (0, index_js_1.getTranslateValues)(elementRef.current);
111
+ const [x, y] = (0, index_js_1.getTranslateValuesFromElement)(elementRef.current);
112
112
  grabOffsetX.current = e.clientX - x;
113
113
  grabOffsetY.current = e.clientY - y;
114
114
  originalUserSelect.current = elementRef.current.style.userSelect;
@@ -4,7 +4,7 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as React from 'react';
6
6
  import cx from 'classnames';
7
- import { FocusTrap, getTranslateValues, Resizer, useMergedRefs, useTheme, useIsomorphicLayoutEffect, } from '../utils/index.js';
7
+ import { FocusTrap, getTranslateValuesFromElement, Resizer, useMergedRefs, useTheme, useIsomorphicLayoutEffect, getTranslateValues, roundByDPR, } from '../utils/index.js';
8
8
  import { useDialogContext } from './DialogContext.js';
9
9
  import { CSSTransition } from 'react-transition-group';
10
10
  import { DialogDragContext } from './DialogDragContext.js';
@@ -35,7 +35,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
35
35
  useTheme();
36
36
  const [style, setStyle] = React.useState();
37
37
  const dialogRef = React.useRef(null);
38
- const refs = useMergedRefs(dialogRef, ref);
38
+ const [dialogElement, setDialogElement] = React.useState();
39
39
  const hasBeenResized = React.useRef(false);
40
40
  const previousFocusedElement = React.useRef();
41
41
  const originalBodyOverflow = React.useRef('');
@@ -89,7 +89,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
89
89
  return;
90
90
  }
91
91
  const rect = (_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
92
- const [translateX, translateY] = getTranslateValues(dialogRef.current);
92
+ const [translateX, translateY] = getTranslateValuesFromElement(dialogRef.current);
93
93
  setStyle((oldStyle) => {
94
94
  var _a, _b;
95
95
  return ({
@@ -108,13 +108,17 @@ export const DialogMain = React.forwardRef((props, ref) => {
108
108
  ...newStyle,
109
109
  }));
110
110
  }, []);
111
+ const roundedTransform = useRoundedTransform({
112
+ element: dialogElement,
113
+ transform,
114
+ });
111
115
  const content = (React.createElement("div", { className: cx('iui-dialog', {
112
116
  'iui-dialog-default': styleType === 'default',
113
117
  'iui-dialog-full-page': styleType === 'fullPage',
114
118
  'iui-dialog-visible': isOpen,
115
119
  'iui-dialog-draggable': isDraggable,
116
- }, className), role: 'dialog', ref: refs, onKeyDown: handleKeyDown, tabIndex: -1, "data-iui-placement": placement, style: {
117
- transform,
120
+ }, className), role: 'dialog', ref: useMergedRefs(dialogRef, ref, setDialogElement), onKeyDown: handleKeyDown, tabIndex: -1, "data-iui-placement": placement, style: {
121
+ transform: roundedTransform,
118
122
  ...style,
119
123
  ...propStyle,
120
124
  }, ...rest },
@@ -147,3 +151,20 @@ export const DialogMain = React.forwardRef((props, ref) => {
147
151
  !trapFocus && content)));
148
152
  });
149
153
  export default DialogMain;
154
+ // ----------------------------------------------------------------------------
155
+ /**
156
+ * Rounds off an element's transform value based on the device's pixel grid, to avoid blurring.
157
+ */
158
+ const useRoundedTransform = ({ element, transform, }) => {
159
+ const [roundedStyles, setRoundedStyles] = React.useState(transform);
160
+ useIsomorphicLayoutEffect(() => {
161
+ if (!element || typeof DOMMatrix === 'undefined') {
162
+ return;
163
+ }
164
+ const [x, y] = transform
165
+ ? getTranslateValues(transform)
166
+ : getTranslateValuesFromElement(element);
167
+ setRoundedStyles(`translate(${roundByDPR(x)}px, ${roundByDPR(y)}px)`);
168
+ }, [element, transform]);
169
+ return roundedStyles;
170
+ };
@@ -3,7 +3,7 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as React from 'react';
6
- import { getBoundedValue, getTranslateValues } from '../functions/index.js';
6
+ import { getBoundedValue, getTranslateValuesFromElement, } from '../functions/index.js';
7
7
  /**
8
8
  * Component that allows to resize parent element.
9
9
  * Parent must have `position: relative`.
@@ -25,7 +25,7 @@ export const Resizer = (props) => {
25
25
  }
26
26
  const initialPointerX = event.clientX;
27
27
  const initialPointerY = event.clientY;
28
- const [initialTranslateX, initialTranslateY] = getTranslateValues(elementRef.current);
28
+ const [initialTranslateX, initialTranslateY] = getTranslateValuesFromElement(elementRef.current);
29
29
  const { width: initialWidth, height: initialHeight } = elementRef.current.getBoundingClientRect();
30
30
  let width = `${initialWidth}px`;
31
31
  let height = `${initialHeight}px`;
@@ -27,4 +27,5 @@ export declare const mergeEventHandlers: <E extends import("react").SyntheticEve
27
27
  * @param element HTML element you want to get translate value of
28
28
  * @returns Translate values in pixels in an array `[translateX, translateY]`
29
29
  */
30
- export declare const getTranslateValues: (element: HTMLElement | null | undefined) => number[];
30
+ export declare const getTranslateValuesFromElement: (element: HTMLElement | null | undefined) => number[];
31
+ export declare const getTranslateValues: (transformValue: string) => number[];
@@ -51,11 +51,14 @@ export const mergeEventHandlers = (...callbacks) => (event) => {
51
51
  * @param element HTML element you want to get translate value of
52
52
  * @returns Translate values in pixels in an array `[translateX, translateY]`
53
53
  */
54
- export const getTranslateValues = (element) => {
54
+ export const getTranslateValuesFromElement = (element) => {
55
55
  if (!element) {
56
56
  return [];
57
57
  }
58
58
  const transformValue = getComputedStyle(element).getPropertyValue('transform');
59
+ return getTranslateValues(transformValue);
60
+ };
61
+ export const getTranslateValues = (transformValue) => {
59
62
  const matrix = new DOMMatrix(transformValue);
60
63
  return [matrix.m41, matrix.m42];
61
64
  };
@@ -6,3 +6,10 @@ export declare const getBoundedValue: (val: number, min: number, max: number) =>
6
6
  * Returns a random value of a given length containing `A-Za-z0-9_-` symbols.
7
7
  */
8
8
  export declare const getRandomValue: (length?: number) => string;
9
+ /**
10
+ * Rounds a pixel value based on the device's pixel ratio. This ensures that values can be
11
+ * placed evenly on the device’s pixel grid, avoiding any blurring.
12
+ *
13
+ * @see https://floating-ui.com/docs/misc#subpixel-and-accelerated-positioning
14
+ */
15
+ export declare const roundByDPR: (value: number) => number;
@@ -19,3 +19,13 @@ export const getRandomValue = (length = 21) => {
19
19
  }
20
20
  return id;
21
21
  };
22
+ /**
23
+ * Rounds a pixel value based on the device's pixel ratio. This ensures that values can be
24
+ * placed evenly on the device’s pixel grid, avoiding any blurring.
25
+ *
26
+ * @see https://floating-ui.com/docs/misc#subpixel-and-accelerated-positioning
27
+ */
28
+ export const roundByDPR = (value) => {
29
+ const dpr = window.devicePixelRatio || 1;
30
+ return Math.round(value * dpr) / dpr;
31
+ };
@@ -3,7 +3,7 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as React from 'react';
6
- import { getTranslateValues, getWindow } from '../functions/index.js';
6
+ import { getTranslateValuesFromElement, getWindow, } from '../functions/index.js';
7
7
  import { useEventListener } from './useEventListener.js';
8
8
  import { useResizeObserver } from './useResizeObserver.js';
9
9
  const getContainerRect = (containerRef) => {
@@ -36,7 +36,7 @@ export const useDragAndDrop = (elementRef, containerRef, enabled = true) => {
36
36
  return;
37
37
  }
38
38
  const { top, right, bottom, left } = (_a = elementRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
39
- let [newTranslateX, newTranslateY] = getTranslateValues(elementRef.current);
39
+ let [newTranslateX, newTranslateY] = getTranslateValuesFromElement(elementRef.current);
40
40
  containerRectRef.current = getContainerRect(containerRef);
41
41
  if (bottom > containerRectRef.current.bottom) {
42
42
  newTranslateY -= bottom - containerRectRef.current.bottom;
@@ -82,7 +82,7 @@ export const useDragAndDrop = (elementRef, containerRef, enabled = true) => {
82
82
  if (!elementRef.current || e.button !== 0 || !enabled) {
83
83
  return;
84
84
  }
85
- const [x, y] = getTranslateValues(elementRef.current);
85
+ const [x, y] = getTranslateValuesFromElement(elementRef.current);
86
86
  grabOffsetX.current = e.clientX - x;
87
87
  grabOffsetY.current = e.clientY - y;
88
88
  originalUserSelect.current = elementRef.current.style.userSelect;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itwin/itwinui-react",
3
- "version": "2.12.17",
3
+ "version": "2.12.18",
4
4
  "author": "Bentley Systems",
5
5
  "license": "MIT",
6
6
  "main": "cjs/index.js",