Skip to content

Commit

Permalink
feat(theme): create mobile theme to dev mobile library (#287)
Browse files Browse the repository at this point in the history
* feat: add dumi-theme-mobile

* fix: test

* feat: add test for mobile theme

* fix: 1、修复切换页面时不触发预览项目变更的问题 2、不需要出现手机窗口的时候出现了 3、窗口和侧边栏菜单应该2选1,不然页面太拥挤

* feat: 使用 src/content 重构代码

* fix: ci test

* test: add device test

* ci: support to preview mobile theme site

* ci: add 404 page for surge preview

* chore: remove iframe onload event

* chore: remove useless 404 plugin

* chore(theme): rename dumi-theme-mobile to theme-mobile

* refactor(theme): update mobile device wrapper

* refactor(theme): handle props change for useLocaleProps theme api

* refactor(theme): update mobile Previewer & content

* fix: qrcode url

* fix: test

* feat: support dumi@1.1.0-beta.16

* feat: add demos layout

* fix: fallback theme builtins path error

* test(theme): fix circular effects for useLocaleProps case

* chore: remove useless dependencies

* test: add demo layout test

* test(theme): remove useless import for mobile theme case

* chore: fix test

* refactor(theme): rename mobile demo layout

* test(theme): improve test coverage for mobile theme

Co-authored-by: PeachScript <scdzwyxst@gmail.com>
  • Loading branch information
xiaohuoni and PeachScript committed Sep 11, 2020
1 parent af2c142 commit af61668
Show file tree
Hide file tree
Showing 18 changed files with 666 additions and 8 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ jobs:
yarn build
yarn doc:build
dist: dist
preview-mobile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: afc163/surge-preview@v1
env:
DUMI_THEME: dumi-theme-mobile
with:
surge_token: ${{ secrets.SURGE_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
build: |
yarn
yarn build
yarn doc:build
dist: dist
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"private": true,
"scripts": {
"dev": "cross-env BROWSER=none node ./packages/dumi/bin/dumi.js dev",
"dev:mobile": "cross-env BROWSER=none DUMI_THEME=dumi-theme-mobile node ./packages/dumi/bin/dumi.js dev",
"watch": "npm run build -- --watch",
"doc:build": "cross-env BROWSER=none node ./packages/dumi/bin/dumi.js build",
"build": "father-build",
Expand Down
4 changes: 3 additions & 1 deletion packages/preset-dumi/src/theme/hooks/useLocaleProps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import useLocaleProps from './useLocaleProps';

describe('theme API: useLocaleProps', () => {
it('should transform props by locale', () => {
const { result } = renderHook(() => useLocaleProps('en-US', { title: 2, 'title_en-US': 1 }));
const { result } = renderHook(props => useLocaleProps('en-US', props), {
initialProps: { title: 2, 'title_en-US': 1 },
});

expect(result.current).toEqual({ title: 1 });
});
Expand Down
2 changes: 1 addition & 1 deletion packages/preset-dumi/src/theme/hooks/useLocaleProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default <T>(locale: string, props: T) => {

useEffect(() => {
setLocaleProps(processor(locale, props));
}, [locale]);
}, [locale, props]);

return localeProps;
};
12 changes: 6 additions & 6 deletions packages/preset-dumi/src/theme/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ export default async () => {
const builtinPath = path.join(modulePath, 'builtins');
const components = fs.existsSync(builtinPath)
? fs
.readdirSync(builtinPath)
.filter(file => /\.(j|t)sx?$/.test(file))
.map(file => ({
identifier: path.parse(file).name,
source: winPath(path.join(theme, 'builtins', file)),
}))
.readdirSync(builtinPath)
.filter(file => /\.(j|t)sx?$/.test(file))
.map(file => ({
identifier: path.parse(file).name,
source: winPath(path.join(theme, 'builtins', file)),
}))
: [];
const fallbacks = REQUIRED_THEME_BUILTINS.reduce((result, name) => {
if (components.every(({ identifier }) => identifier !== name)) {
Expand Down
1 change: 1 addition & 0 deletions packages/preset-dumi/src/utils/getHostPkgAlias.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('getHostPkgAlias', () => {
'dumi',
'@umijs/preset-dumi',
'dumi-theme-default',
"dumi-theme-mobile",
]);
});
});
1 change: 1 addition & 0 deletions packages/theme-default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0-beta.6",
"description": "The official default theme of dumi",
"files": [
"es",
"src"
],
"repository": {
Expand Down
4 changes: 4 additions & 0 deletions packages/theme-mobile/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
cjs: false,
esm: 'babel',
};
40 changes: 40 additions & 0 deletions packages/theme-mobile/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "dumi-theme-mobile",
"version": "1.0.0-beta.0",
"description": "dumi-theme-mobile",
"files": [
"es",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/umijs/dumi"
},
"keywords": [
"dumi",
"father-build",
"umi"
],
"authors": [
"xiaohuoni <448627663@qq.com> (https://github.com/xiaohuoni)"
],
"license": "MIT",
"bugs": "http://github.com/umijs/dumi/issues",
"homepage": "https://github.com/umijs/dumi/tree/master/packages/theme-mobile#readme",
"publishConfig": {
"access": "public"
},
"devDependencies": {
"dumi": "1.x"
},
"dependencies": {
"dumi-theme-default": "1.0.0-beta.0",
"lodash.debounce": "^4.0.8",
"qrcode.react": "^1.0.0",
"umi-hd": "^5.0.1"
},
"peerDependencies": {
"@umijs/preset-dumi": "1.x",
"react": "^16.13.1"
}
}
22 changes: 22 additions & 0 deletions packages/theme-mobile/src/builtins/Previewer.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import (reference) '../style/variables.less';

@media @v-device-show {
.@{prefix}-mobile-previewer {
.@{prefix}-previewer-demo {
display: none;
}

.@{prefix}-previewer-desc,
.@{prefix}-previewer-desc:not([data-title]) + .@{prefix}-previewer-actions {
border-top: none;
}

button[role='source'] {
margin-right: -8px;
width: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
}
}
68 changes: 68 additions & 0 deletions packages/theme-mobile/src/builtins/Previewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useRef, useEffect, useState } from 'react';
import Previewer, { IPreviewerProps } from 'dumi-theme-default/src/builtins/Previewer';
import debounce from 'lodash.debounce';
import './Previewer.less';

export const ACTIVE_MSG_TYPE = 'dumi:scroll-into-demo';

export default (props: IPreviewerProps) => {
const ref = useRef<HTMLDivElement>();
const [previewerProps, setPreviewerProps] = useState<null | IPreviewerProps>(null);
const [isActive, setIsActive] = useState(false);

useEffect(() => {
const isFirstDemo = document.querySelector('.__dumi-default-mobile-previewer') === ref.current;
const handler = debounce(() => {
const scrollTop = document.documentElement.scrollTop + 128;

// post message if scroll into current demo
if (
// fallback to first demo
(isFirstDemo && scrollTop < ref?.current?.offsetTop) ||
// detect scroll position
(scrollTop > ref?.current?.offsetTop &&
scrollTop < ref?.current?.offsetTop + ref?.current?.offsetHeight)
) {
window.postMessage({ type: ACTIVE_MSG_TYPE, value: props.identifier }, '*');
setIsActive(true);
} else {
setIsActive(false);
}
}, 50);

// only render mobile phone when screen max than 960px
if (window?.outerWidth > 960) {
// active source code wrapper if scroll into demo
handler();
window.addEventListener('scroll', handler);

// rewrite props for device mode
setPreviewerProps(
Object.assign({}, props, {
// omit children
children: null,
// show source code
defaultShowCode: true,
// hide external action
hideActions: ['EXTERNAL' as IPreviewerProps['hideActions'][0]].concat(props.hideActions),
}),
);
} else {
// use standard mode if screen min than 960px
setPreviewerProps(props);
}

return () => window.removeEventListener('scroll', handler);
}, []);

return (
<div className="__dumi-default-mobile-previewer" ref={ref}>
{previewerProps && (
<Previewer
className={isActive ? '__dumi-default-previewer-target' : null}
{...previewerProps}
/>
)}
</div>
);
};
139 changes: 139 additions & 0 deletions packages/theme-mobile/src/components/Device.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
@import (reference) '../style/variables.less';

.gen-device-style(@scale) {
width: @s-device-width * @scale;
min-width: @s-device-width * @scale;
height: @s-device-width * @scale * @s-device-ratio;
box-shadow: 0 0 0 @s-device-border-width * @scale #090a0d,
0 0 0 @s-device-shell-width * @scale #9fa3a8,
0 4px 20px @s-device-shell-width * @scale rgba(0, 0, 0, 0.1);
}

.@{prefix}-device {
position: sticky;
top: @s-device-gap-top;
display: flex;
flex-direction: column;
margin-left: @s-content-margin;
width: @s-device-width;
min-width: @s-device-width;
height: @s-device-width * @s-device-ratio;
border-radius: 32px;
overflow: hidden;
.gen-device-style(1);

@media only screen and (max-width: 1440px) {
.gen-device-style(0.9);
}

@media only screen and (max-width: 1360px) {
.gen-device-style(0.8);
}

@media only screen and (max-width: 960px) {
display: none;
}

&[data-mode='site'] {
top: @s-nav-height + @s-device-shell-width + @s-device-gap-top;
}

&-status,
&-action {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 22px;
}

&-status {
height: 30px;
color: #222;
font-size: 12px;
font-weight: 500;
user-select: none;

span {
display: inline-block;
width: 60px;

&:nth-child(2) {
text-align: center;
}
}

// battery
&::after {
content: '';
display: inline-block;
margin-left: 42px;
width: 14px;
height: 5px;
border-radius: 1px;
background: #50d664;
box-shadow: 0 0 0 1px #fff, 0 0 0 2px #999;
}
}

&-action {
height: 40px;
background: #f3f3f3;
border-top: 1px solid #e3e3e3;

> a,
> button {
padding: 0;
width: 16px;
height: 16px;
box-sizing: content-box;
border: 2px solid transparent;
transition: opacity 0.2s, background 0.2s;
outline: none;
cursor: pointer;

&:hover {
opacity: 0.8;
}

&:active {
opacity: 0.9;
}

&[role='refresh'] {
background-position-x: -144px;
}

&[role='open-demo'] {
background-position-x: -126px;
}

&[role='qrcode'] {
position: relative;
z-index: 1;
background-position-x: -218px;

> canvas {
position: absolute;
bottom: 120%;
left: 50%;
border: 4px solid #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
box-sizing: content-box;
transition: all 0.2s ease-in-out;
transform: translateX(-50%) scale(0);
transform-origin: center bottom;
}

&:hover > canvas,
&:focus > canvas {
transform: translateX(-50%) scale(1);
}
}
}
}

> iframe {
flex: 1;
border: 0;
}
}
Loading

0 comments on commit af61668

Please sign in to comment.