diff --git a/.storybook/main.js b/.storybook/main.js index 656089d0..7ea3b8e3 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,7 +1,43 @@ +// https://stackoverflow.com/a/65970945/12377179 + +const path = require("path"); +const fs = require("fs"); + +function getPackageDir(filepath) { + let currDir = path.dirname(require.resolve(filepath)); + while (true) { + if (fs.existsSync(path.join(currDir, "package.json"))) { + return currDir; + } + const { dir, root } = path.parse(currDir); + if (dir === root) { + throw new Error( + `Could not find package.json in the parent directories starting from ${filepath}.` + ); + } + currDir = dir; + } +} + module.exports = { stories: [ "../app/lib/components/**/*.stories.mdx", "../app/lib/components/**/*.stories.@(js|jsx|ts|tsx)", + "**/*.stories.mdx", + "**/*.stories.@(js|jsx|ts|tsx)", ], addons: ["@storybook/addon-links", "@storybook/addon-essentials"], + + webpackFinal: async (config) => ({ + ...config, + resolve: { + ...config.resolve, + alias: { + ...config.resolve.alias, + "@emotion/core": getPackageDir("@emotion/react"), + "@emotion/styled": getPackageDir("@emotion/styled"), + "emotion-theming": getPackageDir("@emotion/react"), + }, + }, + }), }; diff --git a/app/lib/components/animation/animated-check-icon.tsx b/app/lib/components/animated/animated-check-icon.tsx similarity index 100% rename from app/lib/components/animation/animated-check-icon.tsx rename to app/lib/components/animated/animated-check-icon.tsx diff --git a/app/lib/components/animation/animated-progress-bar.tsx b/app/lib/components/animated/animated-progress-bar.tsx similarity index 100% rename from app/lib/components/animation/animated-progress-bar.tsx rename to app/lib/components/animated/animated-progress-bar.tsx diff --git a/app/lib/components/motions/README.md b/app/lib/components/motions/README.md new file mode 100644 index 00000000..cbd275b7 --- /dev/null +++ b/app/lib/components/motions/README.md @@ -0,0 +1,3 @@ +# General motions + +Motions only ! - no symantic ui components allowed here. diff --git a/app/lib/components/motions/index.ts b/app/lib/components/motions/index.ts new file mode 100644 index 00000000..caad0e09 --- /dev/null +++ b/app/lib/components/motions/index.ts @@ -0,0 +1,2 @@ +export * from "./update-hide-by-scroll-position-and-velocity"; +export * from "./smooth-dampings"; diff --git a/app/lib/components/motions/smooth-dampings/index.ts b/app/lib/components/motions/smooth-dampings/index.ts new file mode 100644 index 00000000..8fc88471 --- /dev/null +++ b/app/lib/components/motions/smooth-dampings/index.ts @@ -0,0 +1,4 @@ +export const smooth_damping_hide_motion_transition = { + ease: [0.1, 0.25, 0.3, 1], + duration: 0.6, +}; diff --git a/app/lib/components/motions/update-hide-by-scroll-position-and-velocity/index.ts b/app/lib/components/motions/update-hide-by-scroll-position-and-velocity/index.ts new file mode 100644 index 00000000..7bc33e93 --- /dev/null +++ b/app/lib/components/motions/update-hide-by-scroll-position-and-velocity/index.ts @@ -0,0 +1,107 @@ +import { useState, useEffect, RefObject } from "react"; +import { ScrollMotionValues } from "framer-motion"; +import { useElementScroll } from "framer-motion"; + +export function update_hide_by_scroll_position_and_velocity({ + scrollYProgress, + is_animating_by_intense_scrolling, + on_animating_by_intense_scrolling, + on_change, + options = { + top_sensitivity: 0.01, + bottom_sensitivity: 0.01, + define_intense_velocity: 1, + do_show_on_bottom_hit: true, + }, +}: { + scrollYProgress: ScrollMotionValues["scrollYProgress"]; + is_animating_by_intense_scrolling: boolean; + on_animating_by_intense_scrolling: (v?: true) => void; + on_change: (hide: boolean) => void; + options?: { + top_sensitivity: number; + bottom_sensitivity: number; + define_intense_velocity: number; + do_show_on_bottom_hit: boolean; + }; +}) { + const velocity = scrollYProgress.getVelocity(); + const velocity_abs = Math.abs(velocity); + if ( + // if < 20, this event is not triggered by human, or caused by extremely short scroll area, causing high velocity. + velocity_abs > 20 || + scrollYProgress.get() == scrollYProgress.getPrevious() + ) { + return; + } + const is_intense_scrolling = velocity_abs > options.define_intense_velocity; + const direction = velocity > 0 ? "down" : "up"; // this is ok. velocity can't be 0. + const scroll_progress_percentage = scrollYProgress.get(); + + if (scroll_progress_percentage >= 1 - options.bottom_sensitivity) { + if (options.do_show_on_bottom_hit) { + // bottom = show + on_change(false); + } + } else if (scroll_progress_percentage <= options.top_sensitivity) { + switch (direction) { + // top + down = hide + case "down": + if (!is_intense_scrolling) { + on_change(true); + } + break; + case "up": + // top + up = show + on_change(false); + break; + } + } else { + if (is_intense_scrolling) { + switch (direction) { + // scroll intense + down = hide + case "down": + on_change(true); + break; + // scroll intense + up = show + case "up": + on_animating_by_intense_scrolling(true); + on_change(false); + break; + } + } else { + if (!is_animating_by_intense_scrolling) { + // middle = hide + on_change(true); + } + } + } +} + +export function useScrollTriggeredAnimation(el: RefObject) { + const { scrollYProgress, scrollY } = useElementScroll(el); + const [hide, setHide] = useState(false); + let is_animating_by_intense_scrolling = false; + useEffect(() => { + return scrollYProgress.onChange(() => + update_hide_by_scroll_position_and_velocity({ + scrollYProgress, + is_animating_by_intense_scrolling, + on_animating_by_intense_scrolling: () => { + is_animating_by_intense_scrolling = true; + }, + on_change: (hide) => { + setHide(hide); + }, + options: { + do_show_on_bottom_hit: false, + top_sensitivity: 0.05, + bottom_sensitivity: 0.1, + define_intense_velocity: 50, + }, + }) + ); + }); + + return hide; +} diff --git a/app/lib/components/navigation/navigation-motions.tsx b/app/lib/components/navigation/navigation-motions.tsx new file mode 100644 index 00000000..84f72690 --- /dev/null +++ b/app/lib/components/navigation/navigation-motions.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { smooth_damping_hide_motion_transition } from "../motions"; + +export function AppbarContainerMotion({ + hidden, + children, +}: { + hidden: boolean; + children: JSX.Element | JSX.Element[]; +}) { + /** add this const **/ + const variants_for_container = { + visible: { opacity: 1 }, + hidden: { opacity: 0, height: 0 }, + }; + + return ( + + {children} + + ); +} + +export function AppbarContentMotion({ + hidden, + children, +}: { + hidden: boolean; + children: JSX.Element | JSX.Element[]; +}) { + const variants_for_child = { + visible: { y: 0 }, + hidden: { y: -40 }, + }; + + return ( + + {children} + + ); +} diff --git a/app/lib/components/navigation/primary-workmode-select.tsx b/app/lib/components/navigation/primary-workmode-select.tsx index 9a96f9d3..2e889958 100644 --- a/app/lib/components/navigation/primary-workmode-select.tsx +++ b/app/lib/components/navigation/primary-workmode-select.tsx @@ -1,7 +1,6 @@ -import { css } from "@emotion/react"; -import styled from "@emotion/styled"; import React from "react"; -import { PrimaryWorkmodeSet, WorkMode } from "../../navigation"; +import { PrimaryWorkmodeSet, WorkMode } from "../../routing"; +import { WorkmodeButton } from "./work-mode-button"; export function PrimaryWorkmodeSelect(props: { set: PrimaryWorkmodeSet; @@ -31,57 +30,3 @@ export function PrimaryWorkmodeSelect(props: { ); } - -function WorkmodeButton(props: { - name: string; - active: boolean; - onClick: () => void; -}) { - return ( - <> - - {props.name} - - - ); -} -interface Props { - active: boolean; -} - -const WorkmodeLabel = styled.h3` - display: flex; - text-transform: capitalize; - font-size: 21px; - letter-spacing: 0em; - cursor: pointer; - user-select: none; - - // reset for h3 init style - margin: 0; - - &:first-child { - margin-right: 12px; - } - - ${(props) => - props.active - ? css` - font-weight: 700; - line-height: 26px; - color: #000; - ` - : css` - font-weight: 400; - line-height: 25px; - color: #cfcfcf; - - &:hover { - font-size: 21px; - font-weight: 400; - line-height: 25px; - letter-spacing: 0em; - color: #606060; - } - `} -`; diff --git a/app/lib/components/navigation/secondary-menu-dropdown.tsx b/app/lib/components/navigation/secondary-menu-dropdown.tsx index 2547c270..7ea5abd9 100644 --- a/app/lib/components/navigation/secondary-menu-dropdown.tsx +++ b/app/lib/components/navigation/secondary-menu-dropdown.tsx @@ -1,6 +1,6 @@ import React from "react"; import { useHistory } from "react-router-dom"; -import { WorkMode, WorkScreen } from "../../navigation"; +import { WorkMode, WorkScreen } from "../../routing"; import { SecondaryWorkmodeMenu } from "./secondary-workmode-menu"; type Stage = "production" | "development" | string; diff --git a/app/lib/components/navigation/work-mode-button.tsx b/app/lib/components/navigation/work-mode-button.tsx new file mode 100644 index 00000000..66fd460c --- /dev/null +++ b/app/lib/components/navigation/work-mode-button.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; + +export function WorkmodeButton(props: { + name: string; + active: boolean; + onClick: () => void; +}) { + return ( + <> + + {props.name} + + + ); +} +interface Props { + active: boolean; +} + +const WorkmodeLabel = styled.h3` + display: flex; + text-transform: capitalize; + font-size: 21px; + letter-spacing: 0em; + cursor: pointer; + user-select: none; + + // reset for h3 init style + margin: 0; + + &:first-child { + margin-right: 12px; + } + + ${(props) => + props.active + ? css` + font-weight: 700; + line-height: 26px; + color: #000; + ` + : css` + font-weight: 400; + line-height: 25px; + color: #cfcfcf; + + &:hover { + font-size: 21px; + font-weight: 400; + line-height: 25px; + letter-spacing: 0em; + color: #606060; + } + `} +`; diff --git a/app/lib/components/upload-steps.tsx b/app/lib/components/upload-steps.tsx index 185aaf15..adc6ec10 100644 --- a/app/lib/components/upload-steps.tsx +++ b/app/lib/components/upload-steps.tsx @@ -5,8 +5,8 @@ import { TransparentButtonStyle, } from "@ui/core/button-style"; import { Button } from "@material-ui/core"; -import { AnimatedProgressBar } from "./animation/animated-progress-bar"; -import { AnimatedCheckIcon } from "./animation/animated-check-icon"; +import { AnimatedProgressBar } from "./animated/animated-progress-bar"; +import { AnimatedCheckIcon } from "./animated/animated-check-icon"; import { motion } from "framer-motion"; import { Preview } from "@ui/previewer"; import CheckIcon from "@assistant/icons/check"; diff --git a/app/lib/main/global-state-atoms.ts b/app/lib/main/global-state-atoms.ts new file mode 100644 index 00000000..d2ccfe47 --- /dev/null +++ b/app/lib/main/global-state-atoms.ts @@ -0,0 +1,6 @@ +import { atom } from "recoil"; + +export const hide_navigation = atom({ + key: "hide_navigation", + default: false, +}); diff --git a/app/lib/main/index.tsx b/app/lib/main/index.tsx index cc82e246..871a8e31 100644 --- a/app/lib/main/index.tsx +++ b/app/lib/main/index.tsx @@ -41,7 +41,7 @@ import { saveLayout, updateLayout, get_page_config_by_path, -} from "../navigation"; +} from "../routing"; import { WorkmodeScreenTabs, @@ -96,72 +96,17 @@ function Screen(props: { screen: WorkScreen }) { } } -function TabsLayout(props: { - workmode: WorkMode; - tabIndex: number; - isTabVisible: boolean; - onChange: (index: number, tab: WorkScreen) => void; -}) { - const history = useHistory(); - const { workmode, tabIndex, onChange } = props; - const tabs_as_page_configs = getWorkmodeTabLayout(workmode).map( - (screen, index) => { - const _ = get_page_config(screen); - return { - id: _.id, - name: _.title, - path: _.path, - }; - } - ); - - useEffect(() => { - handleTabChange(tabIndex); - }, []); - - const handleTabChange = (index: number) => { - const screen = tabs_as_page_configs[index]; - onChange(index, screen.id); - history.replace(screen.path); // since it is a movement between tabs, we don't use push. we use replace to avoid the history stack to be too long. - }; - - return ( - <> - {props.isTabVisible && ( - - )} - - {/* the screen's wrapping layout */} -
- - {tabs_as_page_configs.map((v, i) => { - return ( - } - /> - ); - })} - -
- - ); -} +// region global navigation animation state +import { RecoilRoot, useRecoilState } from "recoil"; +import { + AppbarContainerMotion, + AppbarContentMotion, +} from "../components/navigation/navigation-motions"; +import { hide_navigation } from "./global-state-atoms"; +// endregion function TabNavigationApp(props: { savedLayout: NavigationStoreState }) { + const history = useHistory(); const [workmode, setWorkmode] = useState( props.savedLayout.currentWorkmode ); @@ -169,8 +114,9 @@ function TabNavigationApp(props: { savedLayout: NavigationStoreState }) { props.savedLayout.workmodeSet ); const [tabIndex, setTabIndex] = useState(0); + const [screen, setScreen] = useState(); const [expansion, setExpansion] = useState(true); - + const isTabVisible = expansion; const on_workmode_select = (workmode: WorkMode) => { setWorkmode(workmode); setTabIndex(0); @@ -186,6 +132,33 @@ function TabNavigationApp(props: { savedLayout: NavigationStoreState }) { }); }; + // region animation state + const [whole_navigation_hidden] = useRecoilState(hide_navigation); + + useEffect(() => { + handleTabChange(tabIndex); + }, []); + + const handleTabChange = (index: number) => { + const screen = tabs_as_page_configs[index]; + setScreen(screen.id); + on_work_select(index, screen.id); + history.replace(screen.path); // since it is a movement between tabs, we don't use push. we use replace to avoid the history stack to be too long. + }; + + const tabs_as_page_configs = getWorkmodeTabLayout(workmode).map( + (screen, index) => { + const _ = get_page_config(screen); + return { + id: _.id, + name: _.title, + path: _.path, + }; + } + ); + + const shadow_required = screen == WorkScreen.code || !isTabVisible; + return ( // root flex styled container for the whole app
- -
); // @@ -300,30 +310,32 @@ export default function App(props: { platform: TargetPlatform }) { const Router = getDedicatedRouter(); return ( - - {/* @ts-ignore */} - - - {/* # region unique route section */} - {standalone_pages.map((p) => { - return ( - { - return ; - }} - /> - ); - })} - {/* # endregion unique route section */} - {/* dynamic route shall be placed at the last point, since it overwrites other routes */} - - - {/* 👆 this is for preventing blank page on book up. this will be fixed and removed.*/} - - - + + + {/* @ts-ignore */} + + + {/* # region unique route section */} + {standalone_pages.map((p) => { + return ( + { + return ; + }} + /> + ); + })} + {/* # endregion unique route section */} + {/* dynamic route shall be placed at the last point, since it overwrites other routes */} + + + {/* 👆 this is for preventing blank page on book up. this will be fixed and removed.*/} + + + + ); } @@ -340,7 +352,7 @@ function _update_focused_screen_ev(screen: WorkScreen) { ); } -const AppbarWrapper = styled.div` +const PrimaryWorkmodeWrapper = styled.div` display: flex; padding: 0 16px; /* padding: 0 8px; */ diff --git a/app/lib/navigation/README.md b/app/lib/routing/README.md similarity index 100% rename from app/lib/navigation/README.md rename to app/lib/routing/README.md diff --git a/app/lib/navigation/index.ts b/app/lib/routing/index.ts similarity index 100% rename from app/lib/navigation/index.ts rename to app/lib/routing/index.ts diff --git a/app/lib/navigation/layout-preference.ts b/app/lib/routing/layout-preference.ts similarity index 100% rename from app/lib/navigation/layout-preference.ts rename to app/lib/routing/layout-preference.ts diff --git a/app/lib/navigation/navigation-store/README.md b/app/lib/routing/navigation-store/README.md similarity index 100% rename from app/lib/navigation/navigation-store/README.md rename to app/lib/routing/navigation-store/README.md diff --git a/app/lib/navigation/navigation-store/index.ts b/app/lib/routing/navigation-store/index.ts similarity index 100% rename from app/lib/navigation/navigation-store/index.ts rename to app/lib/routing/navigation-store/index.ts diff --git a/app/lib/navigation/navigation-store/save-workmode-work.ts b/app/lib/routing/navigation-store/save-workmode-work.ts similarity index 100% rename from app/lib/navigation/navigation-store/save-workmode-work.ts rename to app/lib/routing/navigation-store/save-workmode-work.ts diff --git a/app/lib/navigation/pages.ts b/app/lib/routing/pages.ts similarity index 100% rename from app/lib/navigation/pages.ts rename to app/lib/routing/pages.ts diff --git a/app/lib/navigation/primary-workmode-selector.ts b/app/lib/routing/primary-workmode-selector.ts similarity index 100% rename from app/lib/navigation/primary-workmode-selector.ts rename to app/lib/routing/primary-workmode-selector.ts diff --git a/app/lib/navigation/release-visibility-preference.ts b/app/lib/routing/release-visibility-preference.ts similarity index 100% rename from app/lib/navigation/release-visibility-preference.ts rename to app/lib/routing/release-visibility-preference.ts diff --git a/app/lib/navigation/router.ts b/app/lib/routing/router.ts similarity index 100% rename from app/lib/navigation/router.ts rename to app/lib/routing/router.ts diff --git a/app/lib/navigation/work-mode.ts b/app/lib/routing/work-mode.ts similarity index 100% rename from app/lib/navigation/work-mode.ts rename to app/lib/routing/work-mode.ts diff --git a/app/lib/navigation/work-screen.ts b/app/lib/routing/work-screen.ts similarity index 100% rename from app/lib/navigation/work-screen.ts rename to app/lib/routing/work-screen.ts diff --git a/figma-core/code-thread/selection.ts b/figma-core/code-thread/selection.ts index ce8c1846..ffce6809 100644 --- a/figma-core/code-thread/selection.ts +++ b/figma-core/code-thread/selection.ts @@ -10,8 +10,8 @@ export let targetNodeId: string; export function onfigmaselectionchange() { // clear the console for better debugging - console.clear(); - console.warn("log cleared. optimized for new build"); + // console.clear(); + // console.warn("log cleared. optimized for new build"); const rawSelections = figma.currentPage.selection; console.log("selection", rawSelections); const selectionType = analyzeSelection(rawSelections); diff --git a/figma/package.json b/figma/package.json index 0ad1ce82..58bfa60c 100644 --- a/figma/package.json +++ b/figma/package.json @@ -10,6 +10,7 @@ "scripts": { "build": "webpack -p --mode=production", "build:dev": "webpack --mode=development", - "watch": "webpack --watch" + "watch": "webpack --watch", + "dev": "yarn watch" } } \ No newline at end of file diff --git a/package.json b/package.json index d65fdf09..a393b616 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "figma-native": "yarn workspace figma-native run webpack:watch", "figma": "yarn workspace figma run build:dev && yarn web", "figma:onlyweb": "yarn workspace web run dev", - "figma:onlyplugin": "yarn workspace figma run build:dev", + "figma:onlyplugin": "yarn workspace figma run dev", "build:figma:prod": "yarn workspace figma run build", "sketch": "yarn workspace sketch run render", "web": "yarn workspace web run dev", diff --git a/packages/app-design-to-code/code-screen.tsx b/packages/app-design-to-code/code-screen.tsx index 076f0936..265b7b8e 100644 --- a/packages/app-design-to-code/code-screen.tsx +++ b/packages/app-design-to-code/code-screen.tsx @@ -19,6 +19,9 @@ import { ImageHostingRepository, } from "@design-sdk/core/assets-repository"; import { Resizable } from "re-resizable"; +import { useScrollTriggeredAnimation } from "app/lib/components/motions"; +import { useSetRecoilState } from "recoil"; +import { hide_navigation } from "app/lib/main/global-state-atoms"; const resizeBarBase = 5; const resizeBarVerPadding = 5; @@ -52,6 +55,13 @@ export function CodeScreen() { set_vanilla_preview_source(inject_assets_source_to_vanilla(v, r)); }; + const code_scrolling_area_ref = useRef(null); + const set_hide_navigation_state = useSetRecoilState(hide_navigation); + const hide = useScrollTriggeredAnimation(code_scrolling_area_ref); + useEffect(() => { + set_hide_navigation_state(hide); + }, [hide]); + return (
- {/* FIXME: add onCopyClicked to code-box */} -
({ + ...config, + resolve: { + ...config.resolve, + alias: { + ...config.resolve.alias, + "@emotion/core": getPackageDir("@emotion/react"), + "@emotion/styled": getPackageDir("@emotion/styled"), + "emotion-theming": getPackageDir("@emotion/react"), + }, + }, + }), +}; diff --git a/packages/ui-mock-browser/.storybook/preview.js b/packages/ui-mock-browser/.storybook/preview.js new file mode 100644 index 00000000..645f52de --- /dev/null +++ b/packages/ui-mock-browser/.storybook/preview.js @@ -0,0 +1,10 @@ + +export const parameters = { + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +} \ No newline at end of file diff --git a/packages/ui-mock-browser/README.md b/packages/ui-mock-browser/README.md new file mode 100644 index 00000000..21b3b3d0 --- /dev/null +++ b/packages/ui-mock-browser/README.md @@ -0,0 +1,3 @@ +# `@ui/mock-browser` + +![](./.design/design-example.png) diff --git a/packages/ui-mock-browser/browser-bar/browser-bar.stories.mdx b/packages/ui-mock-browser/browser-bar/browser-bar.stories.mdx new file mode 100644 index 00000000..de77ea81 --- /dev/null +++ b/packages/ui-mock-browser/browser-bar/browser-bar.stories.mdx @@ -0,0 +1,27 @@ +import { Meta, Story, Preview, Props } from "@storybook/addon-docs/blocks"; +import { BrowserBar } from "../browser-bar"; + + + +# Preview + + + +
+ { + console.log(`clicked isBack : ${isBack}`); + }} + address="loading" + handleRefresh={() => { + console.log("refresh"); + }} + /> +
+
+
+ +# Props + + diff --git a/packages/ui-mock-browser/browser-bar/index.tsx b/packages/ui-mock-browser/browser-bar/index.tsx new file mode 100644 index 00000000..d5b6cdb9 --- /dev/null +++ b/packages/ui-mock-browser/browser-bar/index.tsx @@ -0,0 +1,117 @@ +import React, { ReactNode } from "react"; +import styled from "@emotion/styled"; +import { AddressBar, BrowserNavigationBfButtonGroup } from "../components"; + +type BrowserBarSize = "xl" | "xs"; + +export function BrowserBar({ + closeHistory, + handleHistory, + size = "xl", + pageFavicon, + address, + handleRefresh, +}: { + closeHistory: { + forward: boolean; + back: boolean; + }; + handleHistory: (isBack: boolean) => void; + size?: BrowserBarSize; + pageFavicon: ReactNode; + address: string; + handleRefresh: () => void; +}) { + return ( + + + + + + + + + {/* + */} + + + ); +} + +const Wrapper = styled.div<{ size: BrowserBarSize }>` + display: flex; + justify-content: flex-start; + flex-direction: row; + align-items: center; + min-height: 100vh; + background-color: rgba(255, 255, 255, 1); + box-sizing: border-box; + padding: 12px 16px; + + // checking size xl is 351px xs is 24px + gap: ${(props) => (props.size === "xl" ? "351px" : "24px")}; +`; + +const BrowserNavigationBfButtonGroupWrap = styled.div` + display: flex; + justify-content: flex-start; + align-items: start; + flex: none; + gap: 0; + box-sizing: border-box; +`; + +const AddressBarWrap = styled.div` + min-width: min-content; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + height: 24px; + background-color: rgba(216, 215, 216, 1); + border-radius: 6px; + position: relative; +`; + +const Trailing = styled.div` + position: relative; +`; + +const IconsMdiIosShare = styled.img` + object-fit: cover; + position: absolute; + left: 0px; + top: 0px; + right: 32px; + bottom: 0px; +`; + +const IconsMdiAdd = styled.img` + object-fit: cover; + position: absolute; + left: 32px; + top: 0px; + right: 0px; + bottom: 0px; +`; + +const Wrap = styled.div` + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 12px 16px; +`; diff --git a/packages/ui-mock-browser/components/address-bar.stories.mdx b/packages/ui-mock-browser/components/address-bar.stories.mdx new file mode 100644 index 00000000..d9540ed8 --- /dev/null +++ b/packages/ui-mock-browser/components/address-bar.stories.mdx @@ -0,0 +1,24 @@ +import { Meta, Story, Preview, Props } from "@storybook/addon-docs/blocks"; +import { AddressBar } from "./address-bar"; + + + +# Preview + + + +
+ } + onRefreshClick={() => { + console.log("refresh"); + }} + /> +
+
+
+ +# Props + + diff --git a/packages/ui-mock-browser/components/address-bar.tsx b/packages/ui-mock-browser/components/address-bar.tsx new file mode 100644 index 00000000..c1bffee9 --- /dev/null +++ b/packages/ui-mock-browser/components/address-bar.tsx @@ -0,0 +1,64 @@ +import React, { ReactNode } from "react"; +import styled from "@emotion/styled"; +import { RefreshButton } from "./refresh-button"; +import { MoreHoriz } from "./more-horiz"; + +interface AddressBarProps { + address: string; + icon?: ReactNode; + onRefreshClick: () => void; +} + +export function AddressBar(props: AddressBarProps) { + return ( + + + + {/* */} + + + {props.icon} +
{props.address}
+
+
+ ); +} + +const Wrapper = styled.div` + height: 100%; + width: 100%; + background-color: rgba(216, 215, 216, 1); + border-radius: 6px; + position: relative; +`; + +const Frame168 = styled.div` + display: flex; + justify-content: center; + flex-direction: row; + align-items: center; + gap: 7px; + position: absolute; + top: calc(50% - 8px); + right: 10px; +`; + +const Frame167 = styled.div` + display: flex; + justify-content: flex-start; + flex-direction: row; + align-items: start; + gap: 6px; + box-sizing: border-box; + position: absolute; + left: calc(50% - 36px); + top: calc(50% - 7px); +`; + +const Address = styled.span` + color: rgba(0, 0, 0, 1); + font-size: 12px; + font-weight: 400; + line-height: 100%; + text-align: center; +`; diff --git a/packages/ui-mock-browser/components/browser-navigation-bf-button-group.stories.mdx b/packages/ui-mock-browser/components/browser-navigation-bf-button-group.stories.mdx new file mode 100644 index 00000000..7af08376 --- /dev/null +++ b/packages/ui-mock-browser/components/browser-navigation-bf-button-group.stories.mdx @@ -0,0 +1,59 @@ +import { Meta, Story, Preview, Props } from "@storybook/addon-docs/blocks"; +import { + BrowserNavigationBfButton, + BrowserNavigationBfButtonGroup, +} from "./browser-navigation-bf-button-group"; + + + +# Preview + + + + { + console.log(isBack); + }} + /> + + + { + console.log(isBack); + }} + /> + + + { + console.log(isBack); + }} + /> + { + console.log(isBack); + }} + /> + + + { + console.log(isBack); + }} + /> + + + +# Props + + + diff --git a/packages/ui-mock-browser/components/browser-navigation-bf-button-group.tsx b/packages/ui-mock-browser/components/browser-navigation-bf-button-group.tsx new file mode 100644 index 00000000..d6830820 --- /dev/null +++ b/packages/ui-mock-browser/components/browser-navigation-bf-button-group.tsx @@ -0,0 +1,101 @@ +import React from "react"; +import styled from "@emotion/styled"; +import { css } from "@emotion/react"; +import ForwardArrowIcon from "./icon/forward-arrow"; + +interface ButtonGroupProps { + forwardEnable?: boolean; + backEnable?: boolean; + handleClick: (isBack: boolean) => void; +} +interface BrowserNavigationBfButtonProps { + isBack?: boolean; + hasHistory: boolean; + onClick?: (isBack: boolean) => void; +} + +export function BrowserNavigationBfButton( + props?: BrowserNavigationBfButtonProps +) { + console.log(props.hasHistory); + return ( + props.onClick(!!props.isBack)} + isShow={!props.hasHistory} + disabled={!props.hasHistory} + > + + + + + ); +} + +export function BrowserNavigationBfButtonGroup(props: ButtonGroupProps) { + return ( + + props.handleClick(true)} + hasHistory={props.backEnable} + isBack={true} + /> + props.handleClick(false)} + hasHistory={props.forwardEnable} + /> + + ); +} + +const Wrap = styled.div` + justify-content: flex-start; + flex-direction: row; + align-items: start; + flex: none; + + button { + &:first-child { + margin-right: 6px; + } + } +`; + +const BtnWrap = styled.button<{ isShow: boolean }>` + /* clear button origin style */ + border: 0; + padding: 0; + + position: relative; + width: 28px; + height: 24px; + border-radius: 4px; + + background-color: transparent; + + ${(props) => + props.isShow + ? css` + cursor: default; + opacity: 0; + ` + : css` + cursor: pointer; + `} + + &:hover { + background-color: rgba(242, 242, 242, 1); + border-radius: 4px; + } +`; + +const ArrowIconWrap = styled.span<{ isBack?: boolean }>` + position: absolute; + left: calc(50% - 8px); + top: calc(50% - 8px); + + transform: ${(props) => (props.isBack ? "rotate(180deg)" : "rotate(0deg)")}; + + & > svg { + display: block; + } +`; diff --git a/packages/ui-mock-browser/components/browser-tabs-indications.tsx b/packages/ui-mock-browser/components/browser-tabs-indications.tsx new file mode 100644 index 00000000..56cbb05c --- /dev/null +++ b/packages/ui-mock-browser/components/browser-tabs-indications.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import styled from "@emotion/styled"; + +export function BrowserTabsIndications() { + function PinIcon() { + return ( + + + + + + ); + } + + return <>; +} + +const IconWrap = styled.svg` + &:hover { + fill: #737373; + } +`; + +const PlaceholderIcon = styled.div` + border-radius: 3px; + background: linear-gradient( + 180deg, + rgba(196, 196, 196, 1), + rgba(196, 196, 196, 0) + ); +`; diff --git a/packages/ui-mock-browser/components/icon/forward-arrow.tsx b/packages/ui-mock-browser/components/icon/forward-arrow.tsx new file mode 100644 index 00000000..5b66e969 --- /dev/null +++ b/packages/ui-mock-browser/components/icon/forward-arrow.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +export default function ForwardArrowIcon() { + return ( + <> + + + + + ); +} diff --git a/packages/ui-mock-browser/components/icon/index.ts b/packages/ui-mock-browser/components/icon/index.ts new file mode 100644 index 00000000..8b176d3c --- /dev/null +++ b/packages/ui-mock-browser/components/icon/index.ts @@ -0,0 +1 @@ +export * from "./forward-arrow"; diff --git a/packages/ui-mock-browser/components/index.ts b/packages/ui-mock-browser/components/index.ts new file mode 100644 index 00000000..013266d5 --- /dev/null +++ b/packages/ui-mock-browser/components/index.ts @@ -0,0 +1,8 @@ +export * from "./address-bar"; +export * from "./browser-navigation-bf-button-group"; +export * from "./browser-tabs-indications"; +export * from "./more-horiz"; +export * from "./refresh-button"; +export * from "./trailing"; + +export * from "./icon"; diff --git a/packages/ui-mock-browser/components/more-horiz.tsx b/packages/ui-mock-browser/components/more-horiz.tsx new file mode 100644 index 00000000..4d7ded48 --- /dev/null +++ b/packages/ui-mock-browser/components/more-horiz.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import styled from "@emotion/styled"; + +export function MoreHoriz() { + return ( + + + + + + ); +} + +const IconWrap = styled.span` + cursor: pointer; +`; diff --git a/packages/ui-mock-browser/components/refresh-button.tsx b/packages/ui-mock-browser/components/refresh-button.tsx new file mode 100644 index 00000000..a9b4dc8a --- /dev/null +++ b/packages/ui-mock-browser/components/refresh-button.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import styled from "@emotion/styled"; + +interface RefreshButtonProps { + onClick: () => void; +} + +export function RefreshButton(props?: RefreshButtonProps) { + function RefreshIcon() { + return ( + + + + + + ); + } + + return ( + <> + + + ); +} + +const IconWrap = styled.button` + /* clear button origin style */ + border: 0; + padding: 0; + background-color: transparent; + + opacity: 0; + cursor: pointer; + + &:hover { + opacity: 1; + } +`; diff --git a/packages/ui-mock-browser/components/trailing/add-icon.tsx b/packages/ui-mock-browser/components/trailing/add-icon.tsx new file mode 100644 index 00000000..efc496ab --- /dev/null +++ b/packages/ui-mock-browser/components/trailing/add-icon.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import styled from "@emotion/styled"; + +export function AddIcon() { + return ( + <> + + + + + ); +} diff --git a/packages/ui-mock-browser/components/trailing/index.tsx b/packages/ui-mock-browser/components/trailing/index.tsx new file mode 100644 index 00000000..5d83e9e9 --- /dev/null +++ b/packages/ui-mock-browser/components/trailing/index.tsx @@ -0,0 +1,2 @@ +export * from "./ios-share-icon"; +export * from "./add-icon"; diff --git a/packages/ui-mock-browser/components/trailing/ios-share-icon.tsx b/packages/ui-mock-browser/components/trailing/ios-share-icon.tsx new file mode 100644 index 00000000..789354c9 --- /dev/null +++ b/packages/ui-mock-browser/components/trailing/ios-share-icon.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import styled from "@emotion/styled"; + +export function IosShare() { + return ( + <> + + + + + ); +} diff --git a/packages/ui-mock-browser/index.ts b/packages/ui-mock-browser/index.ts new file mode 100644 index 00000000..9fb51fdb --- /dev/null +++ b/packages/ui-mock-browser/index.ts @@ -0,0 +1 @@ +export * from "./browser-bar"; diff --git a/packages/ui-mock-browser/package.json b/packages/ui-mock-browser/package.json new file mode 100644 index 00000000..c89cd2aa --- /dev/null +++ b/packages/ui-mock-browser/package.json @@ -0,0 +1,15 @@ +{ + "name": "@ui/mock-browser", + "version": "0.0.0", + "private": false, + "devDependencies": { + "@storybook/addon-actions": "^6.3.8", + "@storybook/addon-essentials": "^6.3.8", + "@storybook/addon-links": "^6.3.8", + "@storybook/react": "^6.3.8" + }, + "scripts": { + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook" + } +} diff --git a/packages/ui-previewer/preview-responsive/index.tsx b/packages/ui-previewer/preview-responsive/index.tsx index 92fdf260..241730d8 100644 --- a/packages/ui-previewer/preview-responsive/index.tsx +++ b/packages/ui-previewer/preview-responsive/index.tsx @@ -1,6 +1,6 @@ +import React, { useEffect, useState } from "react"; import styled from "@emotion/styled"; import { useSingleSelection } from "plugin-app"; -import React, { useEffect, useState } from "react"; export interface ResponsivePreviewProps { type: "responsive"; @@ -14,6 +14,8 @@ export interface ResponsivePreviewProps { of?: string; } +const margin = 12; + export function ResponsivePreview({ props, parentWidth, @@ -23,7 +25,6 @@ export function ResponsivePreview({ }) { // TODO: remove me - temporal use const design = useSingleSelection(); - const margin = 12; const [scalefactor, setscalefactor] = useState(0); useEffect(() => { if (design) { @@ -57,4 +58,5 @@ const PlainIframe = styled.iframe<{ scale: number; margin: number }>` border: none; transform: ${(props) => `scale(${props.scale})`}; transform-origin: 0 0; + z-index: -1; `; diff --git a/packages/ui-previewer/preview/index.tsx b/packages/ui-previewer/preview/index.tsx index dcfde87c..07bd4a76 100644 --- a/packages/ui-previewer/preview/index.tsx +++ b/packages/ui-previewer/preview/index.tsx @@ -1,13 +1,14 @@ import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import styled from "@emotion/styled"; -import { css } from "@emotion/react"; import { ResponsivePreview, ResponsivePreviewProps, } from "../preview-responsive"; import { StaticPreview, StaticPreviewProps } from "../preview-static"; import { EmptyState } from "../components"; -import { calc } from "@web-builder/styles"; +import { useScrollTriggeredAnimation } from "app/lib/components/motions"; +import { useSetRecoilState } from "recoil"; +import { hide_navigation } from "app/lib/main/global-state-atoms"; interface PreviewProps { auto?: boolean; @@ -33,6 +34,14 @@ export function Preview(props: Props) { const previewRefWrap = useRef(); const [size, setsize] = useState(undefined); + // region navigation animation global state handling by preview's scolling. + const set_hide_navigation_state = useSetRecoilState(hide_navigation); + const hide = useScrollTriggeredAnimation(previewRefWrap); + useEffect(() => { + set_hide_navigation_state(hide); + }, [hide]); + // endregion + useEffect(() => { if (previewRefWrap.current) { setsize(previewRefWrap.current?.offsetWidth); @@ -42,29 +51,30 @@ export function Preview(props: Props) { const initialPreviewHeight = 200; const previewWrapPadding = 0; return ( - - - + + + <> {props.data || props.auto ? ( <>{size && } ) : (
{props.empty || }
)} -
-
-
+ + + ); } function Content({ props, wrapWidth }: { props: Props; wrapWidth: number }) { switch (props.type) { case "responsive": { - return ; + return ; } case "static": { return ; @@ -78,7 +88,6 @@ const PreviewWrap = styled.div<{ isAutoSizable: boolean; }>` padding: ${(props) => `${props.padding}px`}; - background: #f1f1f1; height: ${(props) => props.isAutoSizable ? `calc(100% - ${props.padding * 2}px)` @@ -90,42 +99,42 @@ const PreviewWrap = styled.div<{ const Render = styled.div` position: relative; width: 100%; - height: auto; - /* text-align: center; */ -`; - -const Container = styled.div` - /* To be deleted later */ - height: 100%; +`; - .preview { - background: #f1f1f1; - height: calc(200px - 24px); - } - - .preview-loading { - width: 100%; - background-color: gray; - } +// const Container = styled.div` +// /* To be deleted later */ - .render { - /* .render height equal .preview height */ - width: 100%; - height: calc(200px - 24px); - text-align: center; - object-fit: contain; - display: table; - } +// /* height: 100%; */ +// /* +// .preview { +// background: #f1f1f1; +// height: calc(200px - 24px); +// } */ +// /* +// .preview-loading { +// width: 100%; +// background-color: gray; +// } */ +// /* +// .render { +// .render height equal .preview height +// width: 100%; +// height: calc(200px - 24px); +// text-align: center; +// object-fit: contain; +// display: table; +// } +// */ - .inner-render { - margin: 0 auto; - display: table-cell; - vertical-align: middle; - } - - .rendering-notify { - color: #adaeb2; - font-size: 18px; - } -`; +// /* .inner-render { +// margin: 0 auto; +// display: table-cell; +// vertical-align: middle; +// } */ +// /* +// .rendering-notify { +// color: #adaeb2; +// font-size: 18px; +// } */ +// `;