Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions editor-packages/editor-preview-vanilla/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2021 Grida Inc, softmarshmallow

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 17 additions & 2 deletions editor-packages/editor-preview-vanilla/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Vanilla preview

> Executable web view of the design (vanilla)

![](./docs/assets/example-of-vanilla-preview-on-grida-assistant.png)

## Installation
Expand All @@ -10,11 +12,24 @@ yarn add @code-editor/vanilla-preview

## Usage

See the [real example on assistant](https://github.com/gridaco/assistant/pull/181).

```tsx
import { VanillaPreview } from "@code-editor/vanilla-preview";
import VanillaPreview from "@code-editor/vanilla-preview";

export default function () {
return <VanillaPreview />;
const _DEFAULT_MARGIN = 12;
const _DEFAULT_SHADOW = "0px 4px 64px rgba(160, 160, 160, 0.18)";
const _DEFAULT_BORDER_RADIUS = 4;

return (
<VanillaPreview
{...previewInfo}
margin={_DEFAULT_MARGIN}
borderRadius={_DEFAULT_BORDER_RADIUS}
boxShadow={_DEFAULT_SHADOW}
/>
);
}
```

Expand Down
4 changes: 4 additions & 0 deletions editor-packages/editor-preview-vanilla/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./lib";
// --------------------
import { default as VanillaPreview } from "./lib";
export default VanillaPreview;
5 changes: 5 additions & 0 deletions editor-packages/editor-preview-vanilla/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./responsive-container";
export * from "./responsive-content-iframe";

import { ResponsiveContainer as VanillaPreview } from "./responsive-container";
export default VanillaPreview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react";
import styled from "@emotion/styled";
import { useComponentSize } from "react-use-size";
import {
ResponsiveContentIframe,
ResponsiveContentIframeProps,
} from "../responsive-content-iframe";

export const Container = styled.div<{ heightscale: number }>`
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
justify-content: center;
flex: 0 1 0;
/* FIXME: this should be a height
// this should work, but the flex is making inner iframe height to shrink.
height: max(${(props) => props.heightscale * 100}%, 100%);
ref:
- https://stackoverflow.com/questions/51288769/scaling-a-flexbox-child-with-transform-leaves-empty-space
- https://www.reddit.com/r/css/comments/q5cvei/css_fitcontent_on_parent_wont_work_for_scaled_item/
*/
min-height: 100%;
`;

export function ResponsiveContainer(props: ResponsiveContentIframeProps) {
const { ref: sizingref, height, width } = useComponentSize();
// TODO: do not remove comments here. these are required for below height calculation.
// DON'T REMOVE
// const [renderheightScaleFactor, setRenderheightScaleFactor] = useState(1);

return (
<Container
ref={sizingref}
heightscale={1}
// DON'T REMOVE
// heightscale={renderheightScaleFactor}
>
<ResponsiveContentIframe
previewInfo={props}
parentSize={{ width, height }}
onScaleChange={() => {}}
// DON'T REMOVE
// onScaleChange={setRenderheightScaleFactor}
/>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import styled from "@emotion/styled";

const _DEFAULT_MARGIN = 0;
const _DEFAULT_SHADOW = "0px 0px 0px transparent";
const _DEFAULT_BORDER_RADIUS = 0;

export interface ResponsiveContentIframeProps {
type: "responsive";
/**
* the vanilla html code or remote embeddable web url;
*/
data?: string;
/**
* show responsive view of.
*/
of?: string;

id: string;

/**
* the origin size of the design
*/
origin_size: {
width: number;
height: number;
};

/**
* margin for the iframe to be placed
*
* @default 12
*/
margin?: number;

/**
* border radius of iframe container
*
* @default 4
*/
borderRadius?: number;

/**
* boxshadow css as string
*
* @default "0px 4px 64px rgba(160, 160, 160, 0.18)"
*/
boxShadow?: string;
}

export function ResponsiveContentIframe({
previewInfo,
parentSize,
onScaleChange,
}: {
onScaleChange: (scale: number) => void;
previewInfo: ResponsiveContentIframeProps;
parentSize: { width: number; height: number };
}) {
const margin = allow_0(previewInfo.margin, _DEFAULT_MARGIN);

const [scalefactor, setscalefactor] = useState(1);
const iframeRef = useRef<HTMLIFrameElement>(undefined);

// dangerously remove scrolling for inner ifram html
// ask: @softmarshmallow
useLayoutEffect(() => {
if (iframeRef.current) {
__dangerously_disable_scroll_in_html_body(iframeRef.current);
}
}, [iframeRef, previewInfo.data]);

useEffect(() => {
if (previewInfo && parentSize.width) {
const _s =
(parentSize.width - margin * 2) / previewInfo.origin_size.width;
const framescale = Math.min(_s, 1);
onScaleChange(framescale);
setscalefactor(framescale);
}
}, [parentSize.width, parentSize.height, previewInfo?.id]);

return (
<PlainIframe
key={previewInfo.id}
id="preview-iframe"
ref={iframeRef}
width={previewInfo?.origin_size?.width ?? 0}
height={previewInfo?.origin_size?.height ?? 0}
sandbox="allow-same-origin"
margin={margin}
borderRadius={allow_0(previewInfo?.borderRadius, _DEFAULT_BORDER_RADIUS)}
boxShadow={previewInfo?.boxShadow ?? _DEFAULT_SHADOW}
inner_view_ready={previewInfo.data !== undefined}
srcDoc={previewInfo.data}
scale={scalefactor}
/>
);
}

/**
* this is a explicit temporary solution to disable iframe content to be scrolling. we aleardy disable scrolling a root element inside the body, but when the element is big and the scale factor is not persice enough, the scrollbar will be shown.
* @ask: @softmarshmallow
* @param iframe
*/
function __dangerously_disable_scroll_in_html_body(iframe: HTMLIFrameElement) {
try {
iframe.contentDocument.getElementsByTagName("body")[0].style.overflow =
"hidden";
} catch (_) {
if (process.env.NODE_ENV === "development") {
console.error("__dangerously_disable_scroll_in_html_body", _);
}
}
}

/**
* allow falsy number `0` as a valid value, + use default value if `null` | `undefined`
* @returns
*/
function allow_0(i: number, defaultValue = 0): number {
return typeof i === "number" ? i : defaultValue;
}

const PlainIframe = styled.iframe<{
scale: number;
margin: number;
borderRadius: number;
boxShadow: string;
inner_view_ready: boolean;
}>`
background: ${(p) => (p.inner_view_ready ? "white" : "transparent")};
box-shadow: ${(p) => p.boxShadow};
outline: none;
overflow: hidden;
border-radius: ${(p) => p.borderRadius}px;
margin: ${(props) => props.margin}px;
border: none;
transform: ${(props) => `scale(${props.scale})`};
/* when height smaller, center center */
/* else, center top */
/* TODO: the logic is incomplete */
transform-origin: center top;
/* transform-origin: center ${(p) => (p.scale < 1 ? "center" : "top")}; */
`;
31 changes: 28 additions & 3 deletions editor-packages/editor-preview-vanilla/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
{
"name": "@code-editor/vanilla-preview",
"version": "0.0.0",
"private": "false"
}
"version": "0.0.2",
"private": false,
"main": "dist/index.js",
"repository": "https://github.com/gridaco/designto-code",
"scripts": {
"clean": "rimraf dist",
"prepack": "yarn clean && tsc"
},
"files": [
"dist",
"LICENSE",
"README.md"
],
"peerDependencies": {
"@emotion/styled": "^11.3.0",
"react": "^17.0.2"
},
"devDependencies": {
"@types/node": "^16.11.7",
"typescript": "^4.4.4"
},
"dependencies": {
"react-use-size": "^2.0.4"
},
"publishConfig": {
"access": "public"
}
}
20 changes: 20 additions & 0 deletions editor-packages/editor-preview-vanilla/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"target": "ES6",
"module": "commonjs",
"jsx": "react",
"esModuleInterop": true,
"declaration": true,
"outDir": "dist",
"experimentalDecorators": true,
"noUnusedLocals": false,
"sourceMap": false
},
"exclude": [
"node_modules",
"**/*.spec.ts",
"dist",
"index.ts" // index.ts is for monorepo use
]
}
1 change: 1 addition & 0 deletions editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@babel/runtime": "^7.14.0",
"@base-sdk/base": "^0.1.0-5",
"@code-editor/preview-pip": "^0.0.2",
"@code-editor/vanilla-preview": "^0.0.2",
"@design-sdk/figma-auth-store": "^0.0.2",
"@designto/code": "0.0.1",
"@emotion/core": "^11.0.0",
Expand Down
1 change: 1 addition & 0 deletions editor/pages/embed/vscode/grida-explorer-preview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
> grida-explorer-preview for gridaco/vscode/grida-explorer-preview
Loading