Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to iTwin 4.7.2 (and newer appui) #73

Merged
merged 9 commits into from
Jun 25, 2024
488 changes: 321 additions & 167 deletions package-lock.json

Large diffs are not rendered by default.

36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,23 @@
},
"dependencies": {
"@bentley/icons-generic-webfont": "1.0.34",
"@itwin/appui-abstract": "4.5.1",
"@itwin/appui-react": "4.12.0",
"@itwin/components-react": "4.12.0",
"@itwin/core-bentley": "4.5.1",
"@itwin/core-common": "4.5.1",
"@itwin/core-frontend": "4.5.1",
"@itwin/core-geometry": "4.5.1",
"@itwin/core-i18n": "4.5.1",
"@itwin/core-markup": "4.5.1",
"@itwin/core-orbitgt": "4.5.1",
"@itwin/core-quantity": "4.5.1",
"@itwin/core-react": "4.12.0",
"@itwin/imodel-components-react": "4.12.0",
"@itwin/appui-abstract": "4.7.2",
"@itwin/appui-react": "4.14.1",
"@itwin/components-react": "4.14.1",
"@itwin/core-bentley": "4.7.2",
"@itwin/core-common": "4.7.2",
"@itwin/core-frontend": "4.7.2",
"@itwin/core-geometry": "4.7.2",
"@itwin/core-i18n": "4.7.2",
"@itwin/core-markup": "4.7.2",
"@itwin/core-orbitgt": "4.7.2",
"@itwin/core-quantity": "4.7.2",
"@itwin/core-react": "4.14.1",
"@itwin/imodel-components-react": "4.14.1",
"@itwin/mobile-sdk-core": "0.22.4",
"@itwin/presentation-common": "4.5.1",
"@itwin/presentation-frontend": "4.5.1",
"@itwin/webgl-compatibility": "4.5.1",
"@itwin/presentation-common": "4.7.2",
"@itwin/presentation-frontend": "4.7.2",
"@itwin/webgl-compatibility": "4.7.2",
"classnames": "^2.2.6",
"react": "^17.0.0",
"react-dom": "^17.0.0",
Expand All @@ -55,8 +55,8 @@
"@itwin/eslint-plugin": "4.0.0-dev.48",
"@svgr/cli": "^6.3.1",
"@types/node": "10.14.1",
"@types/react": "^16.14.17",
"@types/react-dom": "16.8.4",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-redux": "^7.1.19",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
Expand Down
63 changes: 31 additions & 32 deletions src/mobile-ui-react/ActionSheetButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,36 @@ export interface ActionSheetButtonProps extends ActionSheetProps, CommonProps {
* in mobile-sdk-core.
* @public
*/
export class ActionSheetButton extends React.Component<ActionSheetButtonProps> {
constructor(props: ActionSheetButtonProps) {
super(props);
}

public static onClick = async (props: ActionSheetButtonProps, source: React.MouseEvent | DOMRect) => {
const result = await presentActionSheet(props, "currentTarget" in source ? source.currentTarget.getBoundingClientRect() : source);
props.onSelected?.(result);
};

public override render() {
const { iconSpec, size, width, height, iconSize } = this.props;
const onClick = async (event: React.MouseEvent) => {
return ActionSheetButton.onClick(this.props, event);
};
let actualIconSize = iconSize;
if (this.props.iconSize === undefined && this.props.iconSpec === undefined) {
actualIconSize = "16px";
}
return (
<NavigationButton
className={this.props.className}
style={this.props.style}
onClick={onClick}
strokeWidth="1px"
size={size}
width={width}
height={height}
iconSpec={iconSpec || "icon-more-vertical-2"}
iconSize={actualIconSize}
/>
);
export function ActionSheetButton(props: ActionSheetButtonProps) {
const { className, style, iconSpec, size, width, height, iconSize } = props;
let actualIconSize = iconSize;
if (iconSize === undefined && iconSpec === undefined) {
actualIconSize = "16px";
}
return (
<NavigationButton
className={className}
style={style}
onClick={async (event) => { await ActionSheetButton.onClick(props, event); }}
strokeWidth="1px"
size={size}
width={width}
height={height}
iconSpec={iconSpec || "icon-more-vertical-2"}
iconSize={actualIconSize}
/>
);
}

/**
* Act as though an {@link ActionSheetButton} with the given props had been clicked. This will show
* an action sheet using the native UI and call `props.onSelected` with the user's selection.
* @param props The {@link ActionSheetButtonProps} to use to show the action sheet.
* @param source The mouse event that triggered the click (whose target's rectangle will be used
* for the action sheet), or the DOM rectangle of the source component that wants to show the action
* sheet.
*/
ActionSheetButton.onClick = async (props: ActionSheetButtonProps, source: React.MouseEvent | DOMRect) => {
const result = await presentActionSheet(props, "currentTarget" in source ? source.currentTarget.getBoundingClientRect() : source);
props.onSelected?.(result);
};
8 changes: 8 additions & 0 deletions src/mobile-ui-react/MobileUi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,17 @@ function stringSetHas(set: Set<string>, values: ReadonlyArray<string>) {
* A custom React hook function for UiSyncEvents.
* @param handler - The callback function.
* @param eventIds - The optional event ids to handle.
*
* __NOTE__: This function should probably be deprecated, but right now there is no obvious way to
* replace it. Consequently, the following is for information only:
* NOT@deprecated in 0.22.5. UiSyncEventArgs were deprecated in appui-react 4.13.x.
*/
// @todo FIX Remove deprecated usage once appui-react provides a reasonable solution.
// eslint-disable-next-line deprecation/deprecation
export function useSyncUiEvent(handler: (args: UiSyncEventArgs) => void, ...eventIds: ReadonlyArray<string>) {
React.useEffect(() => {
// @todo FIX Remove deprecated usage once appui-react provides a reasonable solution.
// eslint-disable-next-line deprecation/deprecation
return SyncUiEventDispatcher.onSyncUiEvent.addListener((args: UiSyncEventArgs) => {
if (eventIds.length === 0 || stringSetHas(args.eventIds, eventIds)) {
handler(args);
Expand Down
2 changes: 2 additions & 0 deletions src/mobile-ui-react/PanViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export class PanTracker {
return this._vpParentDivId;
}

// @todo FIX Remove deprecated usage once appui-react provides a reasonable solution.
// eslint-disable-next-line deprecation/deprecation
private _onSyncUi = (args: UiSyncEventArgs) => {
if (args.eventIds.has(SessionStateActionId.SetIModelConnection) && this._vpParentDivId) {
let panTracker = PanTracker.getWithKey(this._vpParentDivId);
Expand Down
115 changes: 109 additions & 6 deletions src/mobile-ui-react/ResizablePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,11 @@ export function DraggableComponent(props: DraggableComponentProps) {
setLastPosition(undefined);
props.onDragEnd?.();
};
return <TouchDragHandle className={classnames("mui-draggable-component", props.className)} onDragStart={onDragStart} onDrag={onDrag} lastPosition={lastPosition} onDragEnd={onDragEnd} >
return <TouchDragHandle
className={classnames("mui-draggable-component", props.className)}
onDragStart={onDragStart} onDrag={onDrag}
lastPosition={lastPosition} onDragEnd={onDragEnd}
>
{props.children}
</TouchDragHandle>;
}
Expand Down Expand Up @@ -414,6 +418,48 @@ class TouchCaptor extends React.PureComponent<TouchCaptorProps> {
};
}

// @todo Use the functional version of TouchCaptor below after sufficient testing.
toddsouthenbentley marked this conversation as resolved.
Show resolved Hide resolved
// // 2024-06-24: Converted to a function.
// function TouchCaptor(props: TouchCaptorProps) {
// const { className, children, isTouchStarted, onTouchStart, onTouchMove, onTouchEnd } = props;
// const handleTouchStart = React.useCallback((e: React.TouchEvent<HTMLDivElement>) => {
// onTouchStart?.(e.nativeEvent);
// }, [onTouchStart]);
// const handleDocumentTouchEnd = React.useCallback((e: TouchEvent) => {
// if (!isTouchStarted)
// return;
// onTouchEnd?.(e);
// }, [onTouchEnd, isTouchStarted]);
// const handleDocumentTouchMove = React.useCallback((e: TouchEvent) => {
// if (!isTouchStarted)
// return;
// onTouchMove?.(e);
// }, [onTouchMove, isTouchStarted]);
// React.useEffect(() => {
// document.addEventListener("touchend", handleDocumentTouchEnd);
// document.addEventListener("touchmove", handleDocumentTouchMove);
// return () => {
// document.removeEventListener("touchend", handleDocumentTouchEnd);
// document.removeEventListener("touchmove", handleDocumentTouchMove);
// };
// }, [handleDocumentTouchEnd, handleDocumentTouchMove]);
// const fullClassName = classnames(
// "nz-base-pointerCaptor",
// isTouchStarted && "nz-captured",
// className);
// return (
// <div
// className={fullClassName}
// onTouchStart={handleTouchStart}
// style={props.style}
// role="presentation"
// >
// <div className="nz-overlay" />
// {children}
// </div>
// );
// }

interface TouchDragHandleState {
isPointerDown: boolean;
}
Expand Down Expand Up @@ -441,17 +487,19 @@ class TouchDragHandle extends React.PureComponent<TouchDragHandleProps, TouchDra
};

public override render() {
const { style, children, className, lastPosition } = this.props;
return (
<TouchCaptor
children={this.props.children} // eslint-disable-line react/no-children-prop
className={this.props.className}
isTouchStarted={this.props.lastPosition === undefined ? this.state.isPointerDown : true}
className={className}
isTouchStarted={lastPosition === undefined ? this.state.isPointerDown : true}
// onClick={this._handleClick}
onTouchStart={this._handlePointerDown}
onTouchEnd={this._handlePointerUp}
onTouchMove={this._handlePointerMove}
style={this.props.style}
/>
style={style}
>
{children}
</TouchCaptor>
);
}

Expand Down Expand Up @@ -503,3 +551,58 @@ class TouchDragHandle extends React.PureComponent<TouchDragHandleProps, TouchDra
// this.props.onClick?.();
// }
}

// @todo Use the functional version of TouchDragHandle below after sufficient testing.
// NOTE: appui now has usePointerCaptor, which also supports touches. This whole component might
// be redesigned to use that, but I don't understand usePointerCaptor enough to even know if it is
// possible. If usePointerCaptor can be used, then the TouchCaptor class above is not needed.
// // 2024-06-24: Converted to a function.
// function TouchDragHandle(props: TouchDragHandleProps) {
// const { className, style, lastPosition, onDrag, onDragStart, onDragEnd, children } = props;
// const [ isPointerDown, setIsPointerDown ] = React.useState(false);
// const [ initial, setInitial ] = React.useState<XAndY>();
// const handlePointerDown = React.useCallback((e: TouchEvent) => {
// if (e.touches.length !== 1)
// return;
// setIsPointerDown(true);

// e.preventDefault();
// const touch = e.touches[0];

// setInitial({x: touch.clientX, y: touch.clientY});
// }, []);
// const handlePointerMove = React.useCallback((e: TouchEvent) => {
// if (e.touches.length !== 1)
// return;
// const touch = e.touches[0];
// const current = new Point2d(touch.clientX, touch.clientY);
// if (lastPosition) {
// const dragged = current.minus(lastPosition);
// onDrag?.(dragged);
// return;
// }

// if (initial && current.distance(initial) >= 6) {
// onDragStart?.(initial);
// }
// }, [lastPosition, initial, onDragStart, onDrag]);
// const handlePointerUp = React.useCallback(() => {
// setIsPointerDown(false);
// setInitial(undefined);
// if (lastPosition) {
// onDragEnd?.();
// }
// }, [lastPosition, onDragEnd]);
// return (
// <TouchCaptor
// className={className}
// isTouchStarted={lastPosition === undefined ? isPointerDown : true}
// onTouchStart={handlePointerDown}
// onTouchEnd={handlePointerUp}
// onTouchMove={handlePointerMove}
// style={style}
// >
// {children}
// </TouchCaptor>
// );
// }
2 changes: 1 addition & 1 deletion src/mobile-ui-react/TabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export interface TabsAndStandAlonePanelsAPI {
   * Renders the TabBar and stand-alone panels, this should be called in the parent's rendering section of code.
* @returns A React fragment with the TabBar and the stand-alone panels.
*/
renderTabBarAndPanels: () => JSX.Element;
renderTabBarAndPanels: () => React.JSX.Element;
/** The --mui-bottom-panel-animation-duration CSS variable value in milliseconds. */
openCloseTiming: number;
/** The onAutoClose handler for a resizable panel. */
Expand Down
Loading