Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Enable device based preview (mobile/tablet/desktop) with orientations (landscape/portrait)#1513

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
FalkWolsky merged 2 commits intodevfromfeat/mobile-preview
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletionsclient/packages/lowcoder/package.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,6 +52,7 @@
"file-saver": "^2.0.5",
"github-markdown-css": "^5.1.0",
"hotkeys-js": "^3.8.7",
"html5-device-mockups": "^3.2.1",
"immer": "^9.0.7",
"less": "^4.1.3",
"lodash": "^4.17.21",
Expand All@@ -67,6 +68,7 @@
"react": "^18.2.0",
"react-best-gradient-color-picker": "^3.0.10",
"react-colorful": "^5.5.1",
"react-device-mockups": "^0.1.12",
"react-documents": "^1.2.1",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.4",
Expand Down
13 changes: 13 additions & 0 deletionsclient/packages/lowcoder/src/comps/editorState.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,9 @@ export type CompInfo = {

type SelectSourceType = "editor" | "leftPanel" | "addComp" | "rightPanel";

export type DeviceType = "desktop" | "tablet" | "mobile";
export type DeviceOrientation = "landscape" | "portrait";

/**
* All editor states are placed here and are still immutable.
*
Expand All@@ -56,6 +59,8 @@ export class EditorState {
readonly selectedBottomResType?: BottomResTypeEnum;
readonly showResultCompName: string = "";
readonly selectSource?: SelectSourceType; // the source of select type
readonly deviceType: DeviceType = "desktop";
readonly deviceOrientation: DeviceOrientation = "portrait";

private readonly setEditorState: (
fn: (editorState: EditorState) => EditorState
Expand DownExpand Up@@ -357,6 +362,14 @@ export class EditorState {
this.changeState({ editorModeStatus: newEditorModeStatus });
}

setDeviceType(type: DeviceType) {
this.changeState({ deviceType: type });
}

setDeviceOrientation(orientation: DeviceOrientation) {
this.changeState({ deviceOrientation: orientation });
}

setDragging(dragging: boolean) {
if (this.isDragging === dragging) {
return;
Expand Down
22 changes: 16 additions & 6 deletionsclient/packages/lowcoder/src/comps/hooks/screenInfoComp.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect,useMemo,useState } from "react";
import { hookToStateComp } from "../generators/hookToComp";
import { CanvasContainerID } from "@lowcoder-ee/index.sdk";

enum ScreenTypes {
Mobile = 'mobile',
Expand All@@ -19,9 +20,13 @@ type ScreenInfo = {
}

function useScreenInfo() {
const getDeviceType = () => {
if (window.innerWidth < 768) return ScreenTypes.Mobile;
if (window.innerWidth < 889) return ScreenTypes.Tablet;
const canvasContainer = document.getElementById(CanvasContainerID);
const canvas = document.getElementsByClassName('lowcoder-app-canvas')?.[0];
const canvasWidth = canvasContainer?.clientWidth || canvas?.clientWidth;

const getDeviceType = (width: number) => {
if (width < 768) return ScreenTypes.Mobile;
if (width < 889) return ScreenTypes.Tablet;
return ScreenTypes.Desktop;
}
const getFlagsByDeviceType = (deviceType: ScreenType) => {
Expand All@@ -41,16 +46,17 @@ function useScreenInfo() {

const getScreenInfo = useCallback(() => {
const { innerWidth, innerHeight } = window;
const deviceType = getDeviceType();
const deviceType = getDeviceType(canvasWidth || window.innerWidth);
const flags = getFlagsByDeviceType(deviceType);

return {
width: innerWidth,
height: innerHeight,
canvasWidth,
deviceType,
...flags
};
}, [])
}, [canvasWidth])

const [screenInfo, setScreenInfo] = useState<ScreenInfo>({});

Expand All@@ -64,6 +70,10 @@ function useScreenInfo() {
return () => window.removeEventListener('resize', updateScreenInfo);
}, [ updateScreenInfo ])

useEffect(() => {
updateScreenInfo();
}, [canvasWidth]);

return screenInfo;
}

Expand Down
41 changes: 40 additions & 1 deletionclient/packages/lowcoder/src/pages/common/previewHeader.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,12 +15,17 @@ import ProfileDropdown from "./profileDropdown";
import { trans } from "i18n";
import { Logo } from "@lowcoder-ee/assets/images";
import { AppPermissionDialog } from "../../components/PermissionDialog/AppPermissionDialog";
import { useMemo, useState } from "react";
import {useContext,useMemo, useState } from "react";
import { getBrandingConfig } from "../../redux/selectors/configSelectors";
import { HeaderStartDropdown } from "./headerStartDropdown";
import { useParams } from "react-router";
import { AppPathParams } from "constants/applicationConstants";
import React from "react";
import Segmented from "antd/es/segmented";
import MobileOutlined from "@ant-design/icons/MobileOutlined";
import TabletOutlined from "@ant-design/icons/TabletOutlined";
import DesktopOutlined from "@ant-design/icons/DesktopOutlined";
import { DeviceOrientation, DeviceType, EditorContext } from "@lowcoder-ee/comps/editorState";

const HeaderFont = styled.div<{ $bgColor: string }>`
font-weight: 500;
Expand DownExpand Up@@ -130,6 +135,7 @@ export function HeaderProfile(props: { user: User }) {

const PreviewHeaderComp = () => {
const params = useParams<AppPathParams>();
const editorState = useContext(EditorContext);
const user = useSelector(getUser);
const application = useSelector(currentApplication);
const isPublicApp = useSelector(isPublicApplication);
Expand DownExpand Up@@ -197,9 +203,42 @@ const PreviewHeaderComp = () => {
<HeaderProfile user={user} />
</Wrapper>
);

const headerMiddle = (
<>
{/* Devices */}
<Segmented<DeviceType>
options={[
{ value: 'mobile', icon: <MobileOutlined /> },
{ value: 'tablet', icon: <TabletOutlined /> },
{ value: 'desktop', icon: <DesktopOutlined /> },
]}
value={editorState.deviceType}
onChange={(value) => {
editorState.setDeviceType(value);
}}
/>

{/* Orientation */}
{editorState.deviceType !== 'desktop' && (
<Segmented<DeviceOrientation>
options={[
{ value: 'portrait', label: "Portrait" },
{ value: 'landscape', label: "Landscape" },
]}
value={editorState.deviceOrientation}
onChange={(value) => {
editorState.setDeviceOrientation(value);
}}
/>
)}
</>
);

return (
<Header
headerStart={headerStart}
headerMiddle={headerMiddle}
headerEnd={headerEnd}
style={{ backgroundColor: brandingConfig?.headerColor }}
/>
Expand Down
88 changes: 87 additions & 1 deletionclient/packages/lowcoder/src/pages/editor/editorView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,10 +30,12 @@ import {
UserGuideLocationState,
} from "pages/tutorials/tutorialsConstant";
import React, {
ReactNode,
Suspense,
lazy,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useState,
Expand All@@ -58,6 +60,7 @@ import EditorSkeletonView from "./editorSkeletonView";
import { getCommonSettings } from "@lowcoder-ee/redux/selectors/commonSettingSelectors";
import { isEqual, noop } from "lodash";
import { AppSettingContext, AppSettingType } from "@lowcoder-ee/comps/utils/appSettingContext";
import Flex from "antd/es/flex";
// import { BottomSkeleton } from "./bottom/BottomContent";

const Header = lazy(
Expand DownExpand Up@@ -251,6 +254,13 @@ export const EditorWrapper = styled.div`
flex: 1 1 0;
`;

const DeviceWrapperInner = styled(Flex)`
margin: 20px 0 0;
.screen {
overflow: auto;
}
`;

interface EditorViewProps {
uiComp: InstanceType<typeof UIComp>;
preloadComp: InstanceType<typeof PreloadComp>;
Expand DownExpand Up@@ -298,6 +308,64 @@ const aggregationSiderItems = [
}
];

const DeviceWrapper = ({
deviceType,
deviceOrientation,
children,
}: {
deviceType: string,
deviceOrientation: string,
children: ReactNode,
}) => {
const [Wrapper, setWrapper] = useState<React.ElementType | null>(null);

useEffect(() => {
const loadWrapper = async () => {
if (deviceType === "tablet") {
await import('html5-device-mockups/dist/device-mockups.min.css');
const { IPad } = await import("react-device-mockups");
setWrapper(() => IPad);
} else if (deviceType === "mobile") {
await import('html5-device-mockups/dist/device-mockups.min.css');
const { IPhone7 } = await import("react-device-mockups");
setWrapper(() => IPhone7);
} else {
setWrapper(() => null);
}
};

loadWrapper();
}, [deviceType]);

const deviceWidth = useMemo(() => {
if (deviceType === 'tablet' && deviceOrientation === 'portrait') {
return 700;
}
if (deviceType === 'tablet' && deviceOrientation === 'landscape') {
return 1000;
}
if (deviceType === 'mobile' && deviceOrientation === 'portrait') {
return 400;
}
if (deviceType === 'mobile' && deviceOrientation === 'landscape') {
return 800;
}
}, [deviceType, deviceOrientation]);

if (!Wrapper) return <>{children}</>;

return (
<DeviceWrapperInner justify="center">
<Wrapper
orientation={deviceOrientation}
width={deviceWidth}
>
{children}
</Wrapper>
</DeviceWrapperInner>
);
}

function EditorView(props: EditorViewProps) {
const { uiComp } = props;
const params = useParams<AppPathParams>();
Expand DownExpand Up@@ -416,6 +484,24 @@ function EditorView(props: EditorViewProps) {
uiComp,
]);

const uiCompViewWrapper = useMemo(() => {
if (isViewMode) return uiComp.getView();

return (
<DeviceWrapper
deviceType={editorState.deviceType}
deviceOrientation={editorState.deviceOrientation}
>
{uiComp.getView()}
</DeviceWrapper>
)
}, [
uiComp,
isViewMode,
editorState.deviceType,
editorState.deviceOrientation,
]);

// we check if we are on the public cloud
const isLowCoderDomain = window.location.hostname === 'app.lowcoder.cloud';
const isLocalhost = window.location.hostname === 'localhost';
Expand DownExpand Up@@ -455,7 +541,7 @@ function EditorView(props: EditorViewProps) {
{!hideBodyHeader && <PreviewHeader />}
<EditorContainerWithViewMode>
<ViewBody $hideBodyHeader={hideBodyHeader} $height={height}>
{uiComp.getView()}
{uiCompViewWrapper}
</ViewBody>
<div style={{ zIndex: Layers.hooksCompContainer }}>
{hookCompViews}
Expand Down
21 changes: 21 additions & 0 deletionsclient/yarn.lock
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -11455,6 +11455,13 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"html5-device-mockups@npm:^3.2.1":
version: 3.2.1
resolution: "html5-device-mockups@npm:3.2.1"
checksum: abba0bccc6398313102a9365203092a7c0844879d1b0492168279c516c9462d2a7e016045be565bc183e3405a1ae4929402eaceb1952abdbf16f1580afa68df3
languageName: node
linkType: hard

"http-cache-semantics@npm:^4.1.1":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
Expand DownExpand Up@@ -14159,6 +14166,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
file-saver: ^2.0.5
github-markdown-css: ^5.1.0
hotkeys-js: ^3.8.7
html5-device-mockups: ^3.2.1
http-proxy-middleware: ^2.0.6
immer: ^9.0.7
less: ^4.1.3
Expand All@@ -14175,6 +14183,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
react: ^18.2.0
react-best-gradient-color-picker: ^3.0.10
react-colorful: ^5.5.1
react-device-mockups: ^0.1.12
react-documents: ^1.2.1
react-dom: ^18.2.0
react-draggable: ^4.4.4
Expand DownExpand Up@@ -17672,6 +17681,18 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"react-device-mockups@npm:^0.1.12":
version: 0.1.12
resolution: "react-device-mockups@npm:0.1.12"
peerDependencies:
html5-device-mockups: ^3.2.1
prop-types: ^15.5.4
react: ^15.0.0 || ^16.0.0 || ^17.0.0
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 738e969802c32810c2ca3ca3bd6c9bacf9b3d7adda0569c4f5c7fb1d68bab860ac7bb5a50aa2677d852143cb30ab8520e556c4dc7f53be154fd16ca08a9ba32c
languageName: node
linkType: hard

"react-documents@npm:^1.2.1":
version: 1.2.1
resolution: "react-documents@npm:1.2.1"
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp