├── interactable.ts ├── .gitkeep ├── .vscode │ ├── last.sql │ └── settings.json ├── test │ └── .gitkeep ├── .prettierignore ├── .eslintignore ├── src │ ├── scroll │ │ ├── iscroll │ │ │ └── IScrollView.js │ │ └── pull-to-refresh │ │ │ ├── PullToRefresh.scss │ │ │ ├── PullToRefresh.js │ │ │ └── wptr.js │ ├── index.umd.js │ ├── swiper │ │ ├── README.md │ │ ├── demo │ │ │ ├── demo.js │ │ │ └── scalable-swiper.js │ │ ├── swiper.scss │ │ └── swiper.js │ ├── index.js │ ├── CustomComponent.jsx │ ├── index.css │ ├── scalable-view │ │ ├── demo │ │ │ └── demo.js │ │ ├── README.md │ │ ├── README.en.md │ │ └── ScalableView.js │ └── MonitorPanel.js ├── jsconfig.json ├── .npmignore ├── public │ ├── favicon.ico │ ├── github-icon.png │ ├── static.js │ ├── logo.svg │ ├── manifest.json │ └── index.html ├── postcss.config.js ├── .editorconfig ├── .gitignore ├── README.md ├── .eslintrc ├── LICENSE ├── .babelrc ├── dev-config │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── index.d.ts └── package.json ├── gallery.ts ├── .eslintignore ├── scripts │ ├── tools │ │ ├── publish_pkgs.sh │ │ ├── release_pkgs.sh │ │ ├── start_micro.sh │ │ ├── start_mono.sh │ │ └── upgrade_pkgs.sh │ ├── jest │ │ ├── styleMock.js │ │ ├── fileMock.js │ │ ├── jest.setup.js │ │ ├── jest-puppeteer.config.js │ │ └── jest.config.js │ ├── webpack │ │ ├── webpack.config.umd.js │ │ └── webpack.config.dev.js │ └── template │ │ ├── template.js │ │ └── template.ejs ├── packages │ ├── fc-gallery-core │ │ ├── src │ │ │ └── index.ts │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── tsconfig.json │ │ ├── tsconfig.test.json │ │ ├── scripts │ │ │ ├── jest │ │ │ │ └── jest.config.js │ │ │ └── webpack │ │ │ │ └── webpack.config.umd.js │ │ ├── tsconfig.cjs.json │ │ ├── .vscode │ │ │ └── setting.json │ │ ├── tsconfig.es.json │ │ └── package.json │ └── fc-gallery-react │ │ ├── .eslintignore │ │ ├── postcss.config.js │ │ ├── .huskyrc │ │ ├── src │ │ ├── index.ts │ │ ├── typing.d.ts │ │ ├── utils │ │ │ ├── useLightbox.ts │ │ │ ├── context.tsx │ │ │ └── datetime.ts │ │ ├── components │ │ │ ├── ImageHolder │ │ │ │ └── index.tsx │ │ │ └── Carousel │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ ├── types │ │ │ └── index.ts │ │ └── assets │ │ │ ├── expand.svg │ │ │ └── X.svg │ │ ├── tsconfig.test.json │ │ ├── .lintstagedrc │ │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── index.html │ │ └── service-worker.js │ │ ├── example │ │ ├── typing.d.ts │ │ ├── index.tsx │ │ └── pages │ │ │ ├── App.less │ │ │ └── App.tsx │ │ ├── tsconfig.json │ │ ├── jsconfig.json │ │ ├── tsconfig.cjs.json │ │ ├── .vscode │ │ └── settings.json │ │ ├── scripts │ │ └── webpack │ │ │ ├── webpack.config.resolve.js │ │ │ └── webpack.config.dev.js │ │ ├── tsconfig.es.json │ │ ├── .eslintrc.js │ │ └── package.json ├── postcss.config.js ├── tsconfig.test.json ├── jsconfig.json ├── .gitignore ├── .github │ └── ISSUE_TEMPLATE │ │ ├── custom.md │ │ ├── feature_request.md │ │ └── bug_report.md ├── .vscode │ └── setting.json ├── README.md ├── .eslintrc.js ├── tsconfig.json ├── LICENSE └── package.json ├── react-trello-components.ts └── README.md ├── .github └── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md ├── micro-virtual-view ├── README.md └── packages │ └── react-virtual-view │ └── src │ └── hooks │ ├── useSize │ ├── demo │ │ ├── demo1.tsx │ │ ├── demo3.tsx │ │ └── demo2.tsx │ ├── __tests__ │ │ └── index.test.ts │ ├── index.zh-CN.md │ ├── index.en-US.md │ └── index.ts │ └── useVirtualList │ ├── demo │ ├── demo1.tsx │ └── demo2.tsx │ ├── index.zh-CN.md │ ├── index.en-US.md │ ├── __tests__ │ └── index.test.ts │ └── index.ts ├── LICENSE ├── .gitignore └── README.md /interactable.ts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interactable.ts/.vscode/last.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interactable.ts/test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gallery.ts/.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | scripts/ -------------------------------------------------------------------------------- /gallery.ts/scripts/tools/publish_pkgs.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gallery.ts/scripts/tools/release_pkgs.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interactable.ts/.prettierignore: -------------------------------------------------------------------------------- 1 | *.ejs 2 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/src/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interactable.ts/.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.ejs -------------------------------------------------------------------------------- /gallery.ts/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /gallery.ts/scripts/jest/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | scripts/ -------------------------------------------------------------------------------- /gallery.ts/scripts/jest/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/.eslintignore: -------------------------------------------------------------------------------- 1 | @coverage 2 | dist/ 3 | scripts/ -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /interactable.ts/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../.eslintrc.js") -------------------------------------------------------------------------------- /interactable.ts/src/scroll/iscroll/IScrollView.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default class IScrollView {} 4 | -------------------------------------------------------------------------------- /gallery.ts/scripts/tools/start_micro.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | (cd packages/fc-gallery-react && npm start) -------------------------------------------------------------------------------- /gallery.ts/scripts/tools/start_mono.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | (cd packages/fc-gallery-react && npm start) -------------------------------------------------------------------------------- /interactable.ts/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /interactable.ts/src/index.umd.js: -------------------------------------------------------------------------------- 1 | import CustomComponent from './CustomComponent'; 2 | 3 | export default CustomComponent; 4 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /interactable.ts/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | dev-config/ 3 | public/ 4 | test/ 5 | 6 | !dist 7 | !build/index.umd.js 8 | !build/index.umd.css -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | 3 | export { Carousel } from './components/Carousel'; 4 | -------------------------------------------------------------------------------- /gallery.ts/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /interactable.ts/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/micro-components/master/interactable.ts/public/favicon.ico -------------------------------------------------------------------------------- /interactable.ts/public/github-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/micro-components/master/interactable.ts/public/github-icon.png -------------------------------------------------------------------------------- /interactable.ts/src/swiper/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | iDangerous's [Swiper.js](http://idangero.us/swiper/#.V3aIK5N94_U) as a React component. -------------------------------------------------------------------------------- /react-trello-components.ts/README.md: -------------------------------------------------------------------------------- 1 | # react-trello-components.ts 2 | 3 | # Links 4 | 5 | - https://github.com/KaiSpencer/react-trello-ts 6 | -------------------------------------------------------------------------------- /interactable.ts/public/static.js: -------------------------------------------------------------------------------- 1 | console.info('%c Webpack 4 Demo! 🎶 ', 'color:green; background: #ccc; font-size: 24px; font-weight: bold'); 2 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /gallery.ts/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/scripts/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../../../scripts/jest/jest.config'); 2 | 3 | module.exports = baseConfig; 4 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "{scripts,src,tests}/**/*.{js,jsx,ts,tsx}": [ 3 | "eslint", 4 | "git add" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/micro-components/master/gallery.ts/packages/fc-gallery-react/public/favicon.ico -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/typing.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.less' { 2 | const styles: Record; 3 | export = styles; 4 | } 5 | 6 | declare module '*.svg'; 7 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/utils/useLightbox.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/benhowell/react-grid-gallery/blob/master/src/Gallery.js 2 | 3 | export function useLightbox() {} 4 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/example/typing.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.less' { 2 | const styles: Record; 3 | export = styles; 4 | } 5 | 6 | declare module '*.svg'; 7 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src/**/*"] 7 | } 8 | -------------------------------------------------------------------------------- /gallery.ts/.gitignore: -------------------------------------------------------------------------------- 1 | # Node files 2 | node_modules 3 | 4 | # OS junk files 5 | .DS_Store 6 | 7 | # Project specific stuff 8 | .cache-loader 9 | @coverage 10 | *.log 11 | dist 12 | build -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /interactable.ts/postcss.config.js: -------------------------------------------------------------------------------- 1 | const autoprefixer = require('autoprefixer'); 2 | 3 | module.exports = { 4 | plugins: [autoprefixer({ browsers: ['last 4 versions'], flexbox: 'no-2009' })] 5 | }; 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /gallery.ts/scripts/jest/jest.setup.js: -------------------------------------------------------------------------------- 1 | require('jest-fetch-mock'); 2 | 3 | const { configure } = require('enzyme'); 4 | const Adapter = require('enzyme-adapter-react-16'); 5 | configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /gallery.ts/.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist/cjs", 6 | "declaration": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist/cjs", 6 | "declaration": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gallery.ts/.vscode/setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | { "language": "typescript", "autoFix": true }, 6 | { "language": "typescriptreact", "autoFix": true } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /interactable.ts/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import CustomComponent from './CustomComponent'; 4 | import './index.css'; 5 | 6 | render(, document.querySelector('#root')); 7 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/.vscode/setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | { "language": "typescript", "autoFix": true }, 6 | { "language": "typescriptreact", "autoFix": true } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/tsconfig.es.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es6", 5 | "outDir": "dist/es", 6 | "declaration": true, 7 | "declarationDir": "dist/types" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | { "language": "typescript", "autoFix": true }, 6 | { "language": "typescriptreact", "autoFix": true } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/scripts/webpack/webpack.config.resolve.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | resolve: { 5 | alias: { 6 | '@': path.resolve(__dirname, '../..', 'src/') 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/tsconfig.es.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es6", 5 | "outDir": "dist/es", 6 | "declaration": true, 7 | "declarationDir": "dist/types" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gallery.ts/scripts/tools/upgrade_pkgs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | ncu -u 5 | 6 | (cd ./packages/fc-gallery-core && ncu -u) 7 | (cd ./packages/rtw-bootstrap && ncu -u) 8 | (cd ./packages/fc-gallery-react && ncu -u) 9 | (cd ./packages/rtw-mobx-app && ncu -u) 10 | -------------------------------------------------------------------------------- /interactable.ts/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /gallery.ts/scripts/jest/jest-puppeteer.config.js: -------------------------------------------------------------------------------- 1 | // ps https://github.com/GoogleChrome/puppeteer/issues/3120 2 | module.exports = { 3 | launch: { 4 | args: [ 5 | '--disable-gpu', 6 | '--disable-dev-shm-usage', 7 | '--no-first-run', 8 | '--no-zygote', 9 | '--no-sandbox', 10 | ], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /interactable.ts/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Compiled assets 9 | build 10 | dist 11 | 12 | # Dependency directories 13 | node_modules/ 14 | 15 | # Optional npm cache directory 16 | .npm 17 | 18 | # Optional eslint cache 19 | .eslintcache 20 | 21 | ./stats.json 22 | -------------------------------------------------------------------------------- /interactable.ts/src/CustomComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../public/logo.svg'; 4 | import './index.css'; 5 | 6 | export default () => ( 7 |
8 | React Logo 9 |

Hello React UMD Component

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const baseConfig = require('../../.eslintrc.js'); 3 | 4 | module.exports = { 5 | ...baseConfig, 6 | settings: { 7 | 'import/resolver': { 8 | webpack: { config: path.resolve(__dirname, './scripts/webpack/webpack.config.resolve.js') } 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /interactable.ts/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | React Logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /interactable.ts/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React Webpack", 3 | "name": "React Webpack Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "RTW", 3 | "name": "Micro Frontend Boilerplate", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/scripts/webpack/webpack.config.umd.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const merge = require('webpack-merge'); 3 | 4 | const umdConfig = require('../../../../scripts/webpack/webpack.config.umd'); 5 | 6 | const rootPath = process.cwd(); 7 | 8 | module.exports = merge(umdConfig, { 9 | output: { 10 | library: 'rtwCore' 11 | }, 12 | entry: { 13 | index: path.resolve(rootPath, './src/index.ts') 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/components/ImageHolder/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface IImageHolderProps {} 4 | 5 | export interface IImageHolderState {} 6 | 7 | export default class ImageHolder extends React.Component { 8 | constructor(props: IImageHolderProps) { 9 | super(props); 10 | 11 | this.state = {}; 12 | } 13 | 14 | public render() { 15 | return
; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/scripts/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const merge = require('webpack-merge'); 3 | 4 | const devConfig = require('../../../../scripts/webpack/webpack.config.dev'); 5 | 6 | const config = merge(devConfig, { 7 | entry: { 8 | index: path.resolve(__dirname, '../../example/index') 9 | }, 10 | devServer: { 11 | contentBase: path.resolve(__dirname, '../../public') 12 | } 13 | }); 14 | 15 | module.exports = config; 16 | -------------------------------------------------------------------------------- /interactable.ts/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 5 | Arial, sans-serif; 6 | } 7 | 8 | .App { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | text-align: center; 13 | margin: 2rem auto; 14 | } 15 | 16 | img { 17 | transition: 3s; 18 | height: 100px; 19 | width: 100px; 20 | } 21 | 22 | .App:hover > img { 23 | transform: rotate(1080deg); 24 | } 25 | -------------------------------------------------------------------------------- /interactable.ts/src/swiper/demo/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by apple on 16/7/2. 3 | */ 4 | import React from "react"; 5 | import {render} from "react-dom"; 6 | import {SwiperContainer, SwiperSlide} from "./swiper"; 7 | 8 | render( 14 | 15 |
Slide 1
16 |
17 | 18 |
Slide 2
19 |
20 |
, document.getElementById('root')); 21 | -------------------------------------------------------------------------------- /micro-virtual-view/README.md: -------------------------------------------------------------------------------- 1 | # fc-virtual-list 2 | 3 | # About 4 | 5 | ## Motivation & Credits 6 | 7 | - [react-virtual-list #Project#](https://github.com/developerdizzle/react-virtual-list): Super simple virtualized list React component 8 | 9 | - [react-virtual-draggable-grid #Project#](https://github.com/twils0/react-virtual-draggable-grid): react-virtual-draggable-grid is a heavily customizable, virtual, and draggable grid component. RVDG was inspired by the structure of react-motion, though it runs on pure CSS (inline styles) under the hood. In addition, it's only about 9 KB, when gzipped. 10 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/demo/demo1.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Default usage 3 | * desc: using ref to listen to size change 4 | * 5 | * title.zh-CN: 基本用法 6 | * desc.zh-CN: 使用 ref 监听节点尺寸变化 7 | */ 8 | 9 | import React from 'react'; 10 | import {useSize} from '@umijs/hooks'; 11 | 12 | export default () => { 13 | const [state, ref] = useSize(); 14 | return ( 15 |
16 | try to resize the preview window
17 | dimensions -- width: {state.width} px, height: {state.height} px 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RTW 7 | 8 | 9 | 10 | 11 |
12 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/example/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; 4 | 5 | import 'react-tabs/style/react-tabs.css'; 6 | import { App } from './pages/App'; 7 | 8 | ReactDOM.render( 9 |
10 | 11 | 12 | Carousel 13 | 完整实例 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
, 22 | document.getElementById('root') 23 | ); 24 | -------------------------------------------------------------------------------- /interactable.ts/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | React Webpack App 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/utils/context.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const Context = React.createContext({ lightbox: {} }); 4 | 5 | export const { Consumer, Provider } = Context; 6 | 7 | // This is a HOC function. 8 | // It takes a component... 9 | export function withContext(Component) { 10 | // ...and returns another component... 11 | return props => { 12 | // ... and renders the wrapped component with the context theme! 13 | // Notice that we pass through any additional props as well 14 | return {value => }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface Image { 2 | src?: string; 3 | nano?: string; 4 | alt?: string; 5 | title?: string; 6 | description?: string; 7 | srcSet?: string; 8 | imageSet?: { srcSet: string; media: string; type: string }[]; 9 | sizes?: string; 10 | className?: string; 11 | 12 | thumbnail?: string; 13 | thumbnailLabel?: string; 14 | thumbnailClassName?: string; 15 | thumbnailWidth?: number; 16 | thumbnailHeight?: number; 17 | 18 | bulletClass?: string; 19 | caption?: string; 20 | tags?: { value: string; title: string }[]; 21 | 22 | isSelected?: boolean; 23 | } 24 | -------------------------------------------------------------------------------- /interactable.ts/src/swiper/swiper.scss: -------------------------------------------------------------------------------- 1 | .swiper-container{ 2 | width: 100%; 3 | height: 100%; 4 | margin: 5px auto; 5 | } 6 | 7 | .swiper-slide { 8 | text-align: center; 9 | font-size: 18px; 10 | background: #fff; 11 | 12 | /* Center slide text vertically */ 13 | display: -webkit-box; 14 | display: -ms-flexbox; 15 | display: -webkit-flex; 16 | display: flex; 17 | -webkit-box-pack: center; 18 | -ms-flex-pack: center; 19 | -webkit-justify-content: center; 20 | justify-content: center; 21 | -webkit-box-align: center; 22 | -ms-flex-align: center; 23 | -webkit-align-items: center; 24 | align-items: center; 25 | } -------------------------------------------------------------------------------- /gallery.ts/README.md: -------------------------------------------------------------------------------- 1 | # fc-gallery 2 | 3 | fc-gallery 包含了常见的图片浏览组件。 4 | 5 | # Usage 6 | 7 | 包含了三种模式 8 | 9 | - grid 网格模式 10 | 11 | - carousel 走马灯模式 12 | 13 | - waterfull 瀑布流模式 14 | 15 | # Motivation & Credits 16 | 17 | - [react-image-gallery](https://github.com/xiaolin/react-image-gallery): React carousel image gallery component with thumbnail support 18 | 19 | - [react-photo-gallery](https://github.com/neptunian/react-photo-gallery): Responsive, accessible, composable, and customizable image gallery component 20 | 21 | - [react-grid-gallery](https://github.com/benhowell/react-grid-gallery): Justified image gallery component for React 22 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/demo/demo3.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Listen to pre-rendered DOM 3 | * desc: pass in the DOM element itself 4 | * 5 | * title.zh-CN: 监听提前渲染节点 6 | * desc.zh-CN: 直接传入 dom 节点 7 | */ 8 | 9 | import React from 'react'; 10 | import {useSize} from '@umijs/hooks'; 11 | 12 | export default () => { 13 | const [state] = useSize(document.querySelector('body')); 14 | return ( 15 |
16 | this demo is listening to body size change, try to resize the window instead
17 | dimensions -- width: {state.width} px, height: {state.height} px 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/demo/demo2.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Lazy load DOM element(used to subscibe to dom element renders after the hook) 3 | * desc: pass in a function that returns the DOM element 4 | * 5 | * title.zh-CN: 懒加载(用于监听同一组件内后渲染节点) 6 | * desc.zh-CN: 传入 function 来监听 dom 节点 7 | */ 8 | 9 | import React from 'react'; 10 | import {useSize} from '@umijs/hooks'; 11 | 12 | export default () => { 13 | const [state] = useSize(() => document.querySelector('#demo2')); 14 | return ( 15 |
16 | try to resize the preview window
17 | dimensions -- width: {state.width} px, height: {state.height} px 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /gallery.ts/.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /interactable.ts/src/scalable-view/demo/demo.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import ScalableComponent from '../scalable'; 6 | 7 | render( 8 | 9 |
10 |

HI

11 |

12 | This is Demo For Scalable 13 |

14 | 20 |
21 |
, 22 | document.getElementById('root') 23 | ); 24 | -------------------------------------------------------------------------------- /gallery.ts/scripts/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | coverageDirectory: '/@coverage', 3 | globals: { 4 | 'ts-jest': { 5 | tsConfig: 'tsconfig.test.json' 6 | } 7 | }, 8 | moduleFileExtensions: ['js', 'ts', 'tsx'], 9 | moduleNameMapper: { 10 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 11 | '/scripts/jest/fileMock.js', 12 | '\\.(scss|css|less)$': '/scripts/jest/styleMock.js' 13 | }, 14 | rootDir: '../../', 15 | setupFiles: ['/scripts/jest/jest.setup.js'], 16 | snapshotSerializers: ['enzyme-to-json/serializer'], 17 | testRegex: '/__test__/.+\\.(test|spec)\\.tsx?$', 18 | transform: { '^.+\\.tsx?$': 'ts-jest' }, 19 | verbose: true 20 | }; 21 | -------------------------------------------------------------------------------- /interactable.ts/README.md: -------------------------------------------------------------------------------- 1 | # fc-interactable 2 | 3 | # About 4 | 5 | ## Motivation & Credits 6 | 7 | - [react-resizable #Project#](https://github.com/STRML/react-resizable): A simple React component that is resizable with a handle. 8 | 9 | - [layoutit/grid](https://www.layoutit.com/grid) 10 | 11 | - [interact.js](https://github.com/taye/interact.js): JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+) 12 | 13 | - [reactablejs](https://github.com/beizhedenglong/reactablejs): A react high-order component for interact.js(drag and drop, resizing and multi-touch gestures). 14 | 15 | - [Sizeme #Project#](https://github.com/ctrlplusb/react-sizeme): Make your React Components aware of their width and/or height! 16 | 17 | - https://github.com/hgoebl/mobile-detect.js 18 | -------------------------------------------------------------------------------- /interactable.ts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": ["airbnb", "prettier"], 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "jasmine": true, 8 | "jest": true, 9 | "es6": true 10 | }, 11 | "plugins": ["jest"], 12 | "rules": { 13 | "function-paren-newline": "off", 14 | "import/no-extraneous-dependencies": "off", 15 | "react/no-unused-state": "warn", 16 | "comma-dangle": "off", 17 | "react/forbid-prop-types": "warn", 18 | "react/no-unused-prop-types": "warn", 19 | "react/prop-types": "warn", 20 | "react/self-closing-comp": "warn", 21 | "class-methods-use-this": "off", 22 | "react/jsx-filename-extension": "off", 23 | "react/prefer-stateless-function": "off", 24 | "no-console": "off", 25 | "no-unused-vars": "warn" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gallery.ts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | plugins: ['@typescript-eslint'], 4 | extends: [ 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:prettier/recommended', 7 | 'prettier/@typescript-eslint', 8 | 'prettier/react' 9 | ], 10 | rules: { 11 | '@typescript-eslint/no-var-require': 0, 12 | '@typescript-eslint/interface-name-prefix': 0, 13 | '@typescript-eslint/explicit-member-accessibility': 0, 14 | '@typescript-eslint/no-use-before-define': 0, 15 | '@typescript-eslint/no-empty-interface': 0, 16 | '@typescript-eslint/no-non-null-assertion': 0, 17 | '@typescript-eslint/explicit-function-return-type': 0, 18 | '@typescript-eslint/no-unused-vars': 0, 19 | '@typescript-eslint/no-explicit-any': 0, 20 | '@typescript-eslint/no-empty-function': 0 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/assets/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /gallery.ts/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /gallery.ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "packages", 4 | "lib": ["es6", "es7", "dom", "scripthost", "webworker"], 5 | "jsx": "react", 6 | "target": "es5", 7 | "module": "ESNext", 8 | "moduleResolution": "node", 9 | "sourceMap": true, 10 | "allowJs": false, 11 | "outDir": "dist", 12 | "experimentalDecorators": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "removeComments": true, 15 | "noImplicitReturns": true, 16 | "noImplicitThis": true, 17 | "noImplicitAny": false, 18 | "allowUnusedLabels": true, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "noUnusedLocals": true, 22 | "allowSyntheticDefaultImports": true, 23 | "skipLibCheck": true 24 | }, 25 | "include": ["**/*.js", "**/*.ts", "**/*.tsx"], 26 | "exclude": [ 27 | "assets", 28 | "build", 29 | "dist", 30 | "indep-pkgs", 31 | "node_modules", 32 | "scripts", 33 | "ssr", 34 | "stories", 35 | "__test__", 36 | "test" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/utils/datetime.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export function getFormat(dateMode: string, position?: string) { 4 | switch (dateMode) { 5 | case 'year': 6 | return 'YYYY'; 7 | case 'month': 8 | if (position == 'top') return 'MMMM YYYY'; 9 | else return 'MMMM'; 10 | case 'week': 11 | if (position == 'top') return 'ww MMMM YYYY'; 12 | else return 'ww'; 13 | case 'dayweek': 14 | return 'MM-D'; 15 | case 'daymonth': 16 | return 'D'; 17 | default: 18 | return 'D'; 19 | } 20 | } 21 | 22 | /** 获取某个开始的时间 */ 23 | export const getStartDate = (date, dateMode) => { 24 | let year = null; 25 | 26 | switch (dateMode) { 27 | case 'year': 28 | year = date.year() as any; 29 | return moment([year, 0, 1] as any); 30 | case 'month': 31 | year = date.year(); 32 | const month = date.month(); 33 | return moment([year, month, 1]); 34 | case 'week': 35 | return moment(date).subtract(date.day(), 'days'); 36 | default: 37 | return date; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 王下邀月熊 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gallery.ts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 王下邀月熊 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /interactable.ts/src/MonitorPanel.js: -------------------------------------------------------------------------------- 1 | /** 性能监控 */ 2 | 3 | import React from 'react'; 4 | import Stats from 'stats.js'; 5 | 6 | const { requestAnimationFrame, cancelAnimationFrame } = window; 7 | 8 | class MonitoredStory extends React.Component { 9 | constructor(...args) { 10 | super(...args); 11 | this.rafFn = () => null; // noop 12 | } 13 | 14 | componentDidMount() { 15 | const stats = new Stats(); 16 | stats.showPanel(0); 17 | this.refs.stats.appendChild(stats.dom); 18 | 19 | const animate = () => { 20 | stats.begin(); 21 | this.props.rafFn(); 22 | stats.end(); 23 | this.rafPointer = requestAnimationFrame(animate); 24 | }; 25 | this.rafPointer = requestAnimationFrame(animate); 26 | } 27 | 28 | componentWillUnmount() { 29 | cancelAnimationFrame(this.rafPointer); 30 | } 31 | 32 | render() { 33 | return
{this.props.children}
; 34 | } 35 | } 36 | MonitoredStory.displayName = 'MonitoredStory'; 37 | MonitoredStory.propTypes = { 38 | children: React.PropTypes.node.isRequired, 39 | rafFn: React.PropTypes.func 40 | }; 41 | 42 | export default MonitoredStory; 43 | -------------------------------------------------------------------------------- /interactable.ts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Carloluis Rodriguez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/demo/demo1.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Default usage 3 | * desc: render 100,000 items in a list. 4 | * 5 | * title.zh-CN: 默认用法 6 | * desc.zh-CN: 渲染大量数据 7 | */ 8 | 9 | import React from 'react'; 10 | import { useVirtualList } from '@umijs/hooks'; 11 | 12 | export default () => { 13 | const { list, containerProps, wrapperProps } = useVirtualList(Array.from(Array(99999).keys()), { 14 | overscan: 30, 15 | itemHeight: 60, 16 | }); 17 | return ( 18 | <> 19 |
20 |
21 | {list.map((ele, index) => ( 22 |
33 | Row: {ele.data} 34 |
35 | ))} 36 |
37 |
38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act } from '@testing-library/react-hooks'; 2 | import useSize from '../index'; 3 | 4 | // test about Resize Observer see https://github.com/que-etc/resize-observer-polyfill/tree/master/tests 5 | describe('useSize', () => { 6 | it('should be defined', () => { 7 | expect(useSize).toBeDefined(); 8 | }); 9 | it('without argument ', () => { 10 | const hook = renderHook(() => useSize()); 11 | expect(hook.result.current.length).toEqual(2); 12 | // without args, the init width and height should be undefined 13 | expect(hook.result.current[0].width).toBeUndefined(); 14 | expect(hook.result.current[0].height).toBeUndefined(); 15 | expect(hook.result.current[1].current).toBeUndefined(); 16 | }); 17 | it('with argument', () => { 18 | const hook = renderHook(() => useSize(document.body)); 19 | expect(hook.result.current.length).toEqual(1); 20 | // with args, the init size should be the real size of the element, in node test env, it's 0 though 21 | expect(hook.result.current[0].width).toEqual(0); 22 | expect(hook.result.current[0].height).toEqual(0); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: useSize 3 | nav: 4 | title: Hooks 5 | path: /hooks 6 | group: 7 | title: Dom 8 | path: /dom 9 | legacy: /zh-CN/dom/use-size 10 | --- 11 | 12 | # useSize 13 | 14 | 一个用于监听 dom 节点尺寸变化的 Hook 15 | 16 | ## 代码演示 17 | 18 | ### 基本用法 19 | 20 | 21 | 22 | ### 懒加载(用于监听同一组件内后渲染节点) 23 | 24 | 25 | 26 | ### 监听提前渲染节点 27 | 28 | 29 | 30 | ## API 31 | 32 | ``` 33 | const [ state, ref? ] = useSize(dom); 34 | ``` 35 | 36 | ### Result 37 | 38 | | 参数 | 说明 | 类型 | 39 | |----------|------------------------------------------|------------| 40 | | state | dom 节点的尺寸和位置 | { width: number, height: number } | 41 | | ref | 当未传入任何参数时,将 ref 绑定给需监听的节点 | - | 42 | 43 | ### Params 44 | 45 | | 参数 | 说明 | 类型 | 默认值 | 46 | |---------|----------------------------------------------|------------------------|--------| 47 | | dom? | 可选项,如果未传入则会监听返回结果中的 ref,否则会监听传入的节点 | HTMLElement \| (() => HTMLElement) \| undefined | - | 48 | -------------------------------------------------------------------------------- /interactable.ts/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false, 7 | "useBuiltIns": "usage" 8 | } 9 | ], 10 | "stage-1", 11 | "react" 12 | ], 13 | "plugins": [ 14 | "transform-decorators-legacy", 15 | "react-hot-loader/babel", 16 | [ 17 | "import", 18 | { 19 | "libraryName": "antd", 20 | "style": "css" 21 | } 22 | ] 23 | ], 24 | "env": { 25 | "production": { 26 | "presets": ["react-optimize"], 27 | "plugins": [ 28 | [ 29 | "transform-react-remove-prop-types", 30 | { 31 | "removeImport": true 32 | } 33 | ] 34 | ] 35 | }, 36 | "es-production": { 37 | "presets": [ 38 | [ 39 | "env", 40 | { 41 | "modules": "commonjs", 42 | "useBuiltIns": "usage" 43 | } 44 | ], 45 | "react-optimize" 46 | ], 47 | "plugins": [ 48 | [ 49 | "transform-react-remove-prop-types", 50 | { 51 | "removeImport": true 52 | } 53 | ] 54 | ] 55 | }, 56 | "test": { 57 | "presets": [["env"], "react"] 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /gallery.ts/scripts/webpack/webpack.config.umd.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const prodConfig = require('./webpack.config.prod'); 4 | const rootPath = process.cwd(); 5 | 6 | const plugins = [...prodConfig.plugins]; 7 | 8 | // 移除 CopyWebpackPlugin 与 HtmlWebpackPlugin 9 | plugins.pop(); 10 | plugins.pop(); 11 | 12 | const umdConfig = { 13 | ...prodConfig, 14 | output: { 15 | filename: '[name].js', 16 | path: path.resolve(rootPath, './dist'), 17 | // 默认不允许挂载在全局变量下 18 | // library: library, 19 | libraryTarget: 'umd' 20 | }, 21 | externals: { 22 | 'fc-gallery-core': { 23 | commonjs: 'fc-gallery-core', 24 | commonjs2: 'fc-gallery-core', 25 | amd: 'fc-gallery-core', 26 | root: 'rtwCore' 27 | }, 28 | antd: 'antd', 29 | react: { 30 | commonjs: 'react', 31 | commonjs2: 'react', 32 | amd: 'react', 33 | root: 'React' 34 | }, 35 | 'react-dom': { 36 | commonjs: 'react-dom', 37 | commonjs2: 'react-dom', 38 | amd: 'react-dom', 39 | root: 'ReactDOM' 40 | }, 41 | 'styled-components': { 42 | commonjs: 'styled-components', 43 | commonjs2: 'styled-components' 44 | } 45 | }, 46 | plugins 47 | }; 48 | 49 | delete umdConfig.optimization; 50 | 51 | module.exports = umdConfig; 52 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: useSize 3 | nav: 4 | title: Hooks 5 | path: /hooks 6 | group: 7 | title: Dom 8 | path: /dom 9 | legacy: /dom/use-size 10 | --- 11 | 12 | # useSize 13 | 14 | A hook to subscribe DOM element size change 15 | 16 | ## Examples 17 | 18 | ### Default usage 19 | 20 | 21 | 22 | ### Lazy load DOM element(used to subscibe to DOM element renders after the hook) 23 | 24 | 25 | 26 | ### Listen to pre-rendered DOM 27 | 28 | 29 | 30 | ## API 31 | 32 | ``` 33 | const [ state, ref? ] = useSize(dom); 34 | ``` 35 | 36 | ### Result 37 | 38 | | Property | Description | Type | 39 | |----------|------------------------------------------|------------| 40 | | state | size and position of the DOM | { width: number, height: number } | 41 | | ref | when no param is passed, this ref will be listened | - | 42 | 43 | ### Params 44 | 45 | | Property | Description | Type | Default | 46 | |---------|----------------------------------------------|------------------------|--------| 47 | | dom? | optional, if none is passed, this hook will subscibe to the ref that it returns | HTMLElement \| (() => HTMLElement) \| undefined | - | 48 | -------------------------------------------------------------------------------- /gallery.ts/scripts/template/template.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | // 待渲染完整的页面 3 | // 这里的页面建议以最简形式,如果需要复杂的头设定使用react-helmet组件 4 | 5 | /** 6 | * @function 生成同构直出的HTML界面模板 7 | * @param html 8 | * @param initialState 9 | * @param scripts 10 | * @param styles 11 | * @return {string} 12 | */ 13 | export default (html, initialState = {}, scripts = [], styles = []) => { 14 | return ` 15 | 16 | 17 | 18 | 19 | 20 | ${styleMapper(styles)} 21 | 22 | 23 |
${html}
24 | 25 | 28 | ${scriptMapper(scripts)} 29 | 30 | `; 31 | }; 32 | 33 | /** 34 | * @function 将输入的样式文件路径转化为Link标签 35 | * @param styles 36 | * @return {*} 37 | */ 38 | const styleMapper = styles => { 39 | return styles.map(style => { 40 | return ``; 41 | }); 42 | }; 43 | 44 | const scriptMapper = scripts => { 45 | const scriptTags = []; 46 | 47 | for (let i = 0; i < scripts.length; i = i + 1) { 48 | scriptTags.push(``); 49 | } 50 | 51 | return scriptTags; 52 | }; 53 | -------------------------------------------------------------------------------- /interactable.ts/dev-config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const DashboardPlugin = require('webpack-dashboard/plugin'); 4 | 5 | const baseConfig = require('./webpack.config.base'); 6 | 7 | const config = { 8 | ...baseConfig, 9 | mode: 'development', 10 | devtool: 'eval', 11 | plugins: [ 12 | ...baseConfig.plugins, 13 | 14 | // 在控制台中输出可读的模块名 15 | new webpack.NamedModulesPlugin(), 16 | 17 | // 避免发出包含错误的模块 18 | new webpack.NoEmitOnErrorsPlugin(), 19 | 20 | // 定义控制变量 21 | new webpack.DefinePlugin({ 22 | PRODUCTION: JSON.stringify(false) 23 | }), 24 | new DashboardPlugin() 25 | 26 | // 如果需要启动 DLL 编译,则使用该插件 27 | // new webpack.DllReferencePlugin({ 28 | // manifest: path.resolve(__dirname, '../build/dll/manifest.json') 29 | // }), 30 | ], 31 | devServer: { 32 | // 设置生成的 Bundle 的前缀路径 33 | publicPath: '/', 34 | // assets 中资源文件默认应该还使用 assets 35 | contentBase: path.resolve(__dirname, '../public'), 36 | compress: true, 37 | headers: { 38 | 'X-Content-Type-Options': 'nosniff', 39 | 'X-Frame-Options': 'DENY' 40 | }, 41 | open: true, 42 | overlay: { 43 | warnings: true, 44 | errors: true 45 | }, 46 | host: '0.0.0.0', 47 | port: 8080, 48 | hot: true, 49 | https: false, 50 | disableHostCheck: true, 51 | quiet: false 52 | }, 53 | stats: { 54 | children: false 55 | } 56 | }; 57 | 58 | delete config.extra; 59 | 60 | module.exports = config; 61 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/index.zh-CN.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: useVirtualList 3 | nav: 4 | title: Hooks 5 | path: /hooks 6 | group: 7 | title: UI 8 | path: /ui 9 | legacy: /zh-CN/ui/use-virtual-list 10 | --- 11 | 12 | # useVirtualList 13 | 14 | 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。 15 | 16 | ## 代码演示 17 | 18 | ### 默认用法 19 | 20 | 21 | 22 | ### 动态元素高度 23 | 24 | 25 | 26 | ## API 27 | 28 | ```typescript 29 | const result:Result = useVirtualList(originalList: any[], Options); 30 | ``` 31 | 32 | ### Result 33 | 34 | | 参数 | 说明 | 类型 | 35 | |----------|------------------------------------------|------------| 36 | | list | 当前需要展示的列表内容 | {data: T, index: number}[] | 37 | | containerProps | 滚动容器的 props | {} | 38 | | wrapperProps | children 外层包裹器 props | {} | 39 | | scrollTo | 快速滚动到指定 index | (index: number) => void | 40 | 41 | ### Params 42 | 43 | | 参数 | 说明 | 类型 | 默认值 | 44 | |---------|----------------------------------------------|------------------------|--------| 45 | | originalList | 包含大量数据的列表 | T[] | [] | 46 | | options | 可选配置项,见 Options | - | - | 47 | 48 | 49 | ### Options 50 | 51 | | 参数 | 说明 | 类型 | 默认值 | 52 | |------|--------------|--------|--------| 53 | | itemHeight | 行高度,静态高度可以直接写入像素值,动态高度可传入函数 | number \| ((index: number) => number) | - | 54 | | overscan | 视区上、下额外展示的 dom 节点数量 | number | 10 | 55 | -------------------------------------------------------------------------------- /gallery.ts/scripts/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 3 | const webpack = require('webpack'); 4 | const path = require('path'); 5 | const DashboardPlugin = require('webpack-dashboard/plugin'); 6 | 7 | const baseConfig = require('./webpack.config.base'); 8 | 9 | const config = { 10 | ...baseConfig, 11 | mode: 'development', 12 | devtool: 'source-map', 13 | plugins: [ 14 | ...baseConfig.plugins, 15 | 16 | // 在控制台中输出可读的模块名 17 | new webpack.NamedModulesPlugin(), 18 | 19 | // 避免发出包含错误的模块 20 | new webpack.NoEmitOnErrorsPlugin(), 21 | 22 | // 定义控制变量 23 | new webpack.DefinePlugin({ 24 | isProd: JSON.stringify(false) 25 | }), 26 | new DashboardPlugin() 27 | ], 28 | devServer: { 29 | allowedHosts: ['0.0.0.0:8081'], 30 | // 设置生成的 Bundle 的前缀路径 31 | publicPath: '/', 32 | // assets 中资源文件默认应该还使用 assets 33 | contentBase: path.resolve(__dirname, '../../public'), 34 | compress: true, 35 | headers: { 36 | 'Access-Control-Allow-Origin': '*', 37 | 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 38 | 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization', 39 | 'X-Content-Type-Options': 'nosniff', 40 | 'X-Frame-Options': 'DENY' 41 | }, 42 | open: true, 43 | overlay: { 44 | warnings: true, 45 | errors: true 46 | }, 47 | host: '0.0.0.0', 48 | port: 8081, 49 | hot: true, 50 | https: false, 51 | disableHostCheck: true, 52 | quiet: false 53 | }, 54 | stats: { 55 | children: false 56 | } 57 | }; 58 | 59 | delete config.extra; 60 | 61 | module.exports = config; 62 | -------------------------------------------------------------------------------- /interactable.ts/src/swiper/demo/scalable-swiper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by apple on 16/7/4. 3 | */ 4 | import React from 'react'; 5 | import { render } from 'react-dom'; 6 | import ScalableComponent from '../scalable'; 7 | import { SwiperContainer, SwiperSlide } from '../../scroll/slider/swiper'; 8 | 9 | render( 10 | 11 | 18 | 19 |
20 |

HI Slide1

21 |

22 | This is Demo For Scalable 23 |

24 | 30 |
31 |
32 | 33 | 34 |
35 |

HI Slide2

36 |

37 | This is Demo For Scalable 38 |

39 | 45 |
46 |
47 |
48 |
, 49 | document.getElementById('root') 50 | ); 51 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/assets/X.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | X 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useSize/index.ts: -------------------------------------------------------------------------------- 1 | import { useState, useRef, MutableRefObject, useLayoutEffect } from 'react'; 2 | import ResizeObserver from 'resize-observer-polyfill'; 3 | 4 | type Arg = HTMLElement | (() => HTMLElement) | null; 5 | 6 | type Size = { width?: number; height?: number }; 7 | 8 | function useSize(): [Size, MutableRefObject]; 9 | function useSize(arg: Arg): [Size]; 10 | function useSize( 11 | ...args: [Arg] | [] 12 | ): [Size, MutableRefObject?] { 13 | const hasPassedInElement = args.length === 1; 14 | const arg = useRef(args[0]); 15 | [arg.current] = args; 16 | const element = useRef(); 17 | const [state, setState] = useState(() => { 18 | const initDOM = typeof arg.current === 'function' ? arg.current() : arg.current; 19 | return { 20 | width: (initDOM || {}).clientWidth, 21 | height: (initDOM || {}).clientHeight, 22 | }; 23 | }); 24 | 25 | useLayoutEffect(() => { 26 | const passedInElement = typeof arg.current === 'function' ? arg.current() : arg.current; 27 | const targetElement = hasPassedInElement ? passedInElement : element.current; 28 | if (!targetElement) { 29 | return () => {}; 30 | } 31 | 32 | const resizeObserver = new ResizeObserver(entries => { 33 | entries.forEach(entry => { 34 | setState({ 35 | width: entry.target.clientWidth, 36 | height: entry.target.clientHeight, 37 | }); 38 | }); 39 | }); 40 | 41 | resizeObserver.observe(targetElement); 42 | return () => { 43 | resizeObserver.disconnect(); 44 | }; 45 | }, [element.current, typeof arg.current === 'function' ? undefined : arg.current]); 46 | 47 | if (hasPassedInElement) { 48 | return [state]; 49 | } 50 | return [state, element as MutableRefObject]; 51 | } 52 | 53 | export default useSize; 54 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fc-gallery-core", 3 | "version": "0.0.1", 4 | "description": "fc-gallery-core", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/wx-chevalier/fractal-components" 8 | }, 9 | "license": "MIT", 10 | "main": "dist/cjs/index.js", 11 | "module": "dist/es/index.js", 12 | "types": "dist/types/index.d.ts", 13 | "keywords": [ 14 | "webpack", 15 | "react" 16 | ], 17 | "author": "wx-chevalier@github", 18 | "prettier": { 19 | "printWidth": 100, 20 | "singleQuote": true 21 | }, 22 | "lint-staged": { 23 | "*.{ts,tsx,scss,less,md}": [ 24 | "prettier --write", 25 | "git add" 26 | ] 27 | }, 28 | "scripts": { 29 | "clean": "rimraf dist", 30 | "build": "npm run build:es && npm run build:cjs && npm run build:umd", 31 | "build:es": "tsc --project ./tsconfig.es.json", 32 | "build:cjs": "tsc --project ./tsconfig.cjs.json", 33 | "dev": "tsc -w --project ./tsconfig.cjs.json", 34 | "build:umd": "webpack --config ./scripts/webpack/webpack.config.umd.js", 35 | "test": "jest --config ./scripts/jest/jest.config.js", 36 | "test:watch": "npm test -- --watch", 37 | "test:cov": "npm run cleanCov && npm test -- --coverage" 38 | }, 39 | "dependencies": {}, 40 | "devDependencies": { 41 | "@typescript-eslint/eslint-plugin": "^2.0.0", 42 | "@typescript-eslint/parser": "^2.0.0", 43 | "eslint": "^6.2.2", 44 | "eslint-config-airbnb": "^18.0.1", 45 | "eslint-config-prettier": "^6.1.0", 46 | "eslint-loader": "3.0.0", 47 | "eslint-plugin-import": "^2.18.2", 48 | "eslint-plugin-jsx-a11y": "^6.2.3", 49 | "eslint-plugin-prettier": "^3.1.0", 50 | "eslint-plugin-react": "^7.14.3", 51 | "ts-jest": "^24.0.2", 52 | "typescript": "^3.5.3", 53 | "webpack": "^4.39.2", 54 | "webpack-dev-server": "^3.8.0", 55 | "webpack-merge": "^4.2.1" 56 | }, 57 | "files": [ 58 | "dist/" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/index.en-US.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: useVirtualList 3 | nav: 4 | title: Hooks 5 | path: /hooks 6 | group: 7 | title: UI 8 | path: /ui 9 | legacy: /ui/use-virtual-list 10 | --- 11 | 12 | # useVirtualList 13 | 14 | A hook that allows you to use virtual list to render huge chunks of list data. 15 | 16 | ## Examples 17 | 18 | ### Default usage 19 | 20 | 21 | 22 | ### Dynamic item height 23 | 24 | 25 | 26 | ## API 27 | 28 | ```typescript 29 | const result:Result = useVirtualList(originalList: any[], Options); 30 | ``` 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | |----------|------------------------------------------|------------| 36 | | list | The current portion of data need to be rendered to DOM | {data: T, index: number}[] | 37 | | containerProps | the props of outter container | {} | 38 | | wrapperProps | the props of inner wrapper | {} | 39 | | scrollTo | scroll to specific index | (index: number) => void | 40 | 41 | ### Params 42 | 43 | | Property | Description | Type | Default | 44 | |---------|----------------------------------------------|------------------------|--------| 45 | | originalList | The original list that contains a lot of data entries | T[] | [] | 46 | | options | Optional configuration item, see Options | - | - | 47 | 48 | 49 | ### Options 50 | 51 | | Property | Description | Type | Default | 52 | |------|--------------|--------|--------| 53 | | itemHeight | item height, accept a pixel value or a function that returns the height | number \| ((index: number) => number) | - | 54 | | overscan | the extra buffer items outside of the view area | number | 10 | 55 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/demo/demo2.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: Dynamic item height 3 | * desc: specify item height dynamically. 4 | * 5 | * title.zh-CN: 动态元素高度 6 | * desc.zh-CN: 动态指定每个元素的高度 7 | */ 8 | 9 | import React from 'react'; 10 | import { InputNumber, Button } from 'antd'; 11 | import { useVirtualList } from '@umijs/hooks'; 12 | 13 | export default () => { 14 | const [value, onChange] = React.useState(undefined); 15 | const { list, containerProps, wrapperProps, scrollTo } = useVirtualList( 16 | Array.from(Array(99999).keys()), 17 | { 18 | itemHeight: i => (i % 2 === 0 ? 42 + 8 : 84 + 8), 19 | overscan: 10, 20 | }, 21 | ); 22 | return ( 23 |
24 |
25 | onChange(e)} 32 | /> 33 | 41 |
42 |
43 |
44 | {list.map((ele, index) => ( 45 |
56 | Row: {ele.data} size: {ele.index % 2 === 0 ? 'small' : 'large'} 57 |
58 | ))} 59 |
60 |
61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![default](https://user-images.githubusercontent.com/5803001/40461033-032dbeae-5f3c-11e8-8556-344bfe8ebefe.png) 2 | 3 | [中文版本](./README.md) | [English Version](./REAMDE-en.md) 4 | 5 | # Fractal Components For Awesome UI 6 | 7 | fractal-components contains lots of components, builded during my work. 8 | 9 | --- 10 | 11 | fractal-components 是笔者日常工作中总结出来的应用、组件库以及组件开发模式,为了保证其独立性与复用性,笔者以不同的方式实现了组件。对于各个前端界面框架/库的基础使用可以参考 [fe-boilerplate, 多技术栈前端项目模板](https://github.com/wxyyxc1992/fe-boilerplate);对于组件层次化分类标准可以参考 [Web Widget MindMap](https://parg.co/oC3)。 12 | 13 | fractal-components 中包含了可复用的组件,还有很多纯界面化展示的组件,可以参考 [CodePen](https://codepen.io/dashboard/),或者 [Stackblitz](https://stackblitz.com/@wxyyxc1992),或者 [Code Sandbox](https://codesandbox.io/u/wx-chevalier)。 14 | 15 | # Nav | 导航 16 | 17 | - 关联子项目 18 | 19 | | Name / 名称 | Description / 描述 | Links / 链接 | 20 | | -------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 21 | | web-whiteboard | Electronic Whiteboard for screen sharing / 电子白板 | [Demo](https://3q1z35q53p.codesandbox.io/) / [Source](https://github.com/wx-chevalier/web-whiteboard) | 22 | | fc-schedule | React Components for APS / 调度与排程组件 | [Demo](https://3q1z35q53p.codesandbox.io/) / [Source](https://github.com/wx-chevalier/fc-schedule) | 23 | 24 | # About 25 | 26 | ![FC Logo](https://coding.net/u/hoteam/p/Cache/git/raw/master/2017/6/1/fractal-components-icon.png) 27 | 28 | ## Copyright & More | 延伸阅读 29 | 30 | 笔者所有文章遵循 [知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。如果觉得本系列对你有所帮助,欢迎给我家布丁买点狗粮(支付宝扫码)~ 31 | 32 | ![技术视野](https://s2.ax1x.com/2019/12/03/QQJLvt.png) 33 | 34 | 您还可以前往 [NGTE Books](https://ng-tech.icu/books/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表: 35 | 36 | ![NGTE Books](https://s2.ax1x.com/2020/01/18/19uXtI.png) 37 | 38 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/public/service-worker.js: -------------------------------------------------------------------------------- 1 | /* globals workbox */ 2 | workbox.core.setCacheNameDetails({ 3 | prefix: 'antd-pro', 4 | suffix: 'v1' 5 | }); 6 | // Control all opened tabs ASAP 7 | workbox.clientsClaim(); 8 | 9 | /** 10 | * Use precaching list generated by workbox in build process. 11 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching 12 | */ 13 | workbox.precaching.precacheAndRoute(self.__precacheManifest || []); 14 | 15 | /** 16 | * Register a navigation route. 17 | * https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route 18 | */ 19 | workbox.routing.registerNavigationRoute('/index.html'); 20 | 21 | /** 22 | * Use runtime cache: 23 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute 24 | * 25 | * Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc. 26 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies 27 | */ 28 | 29 | /** 30 | * Handle API requests 31 | */ 32 | workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst()); 33 | 34 | /** 35 | * Handle third party requests 36 | */ 37 | workbox.routing.registerRoute( 38 | /^https:\/\/gw.alipayobjects.com\//, 39 | workbox.strategies.networkFirst() 40 | ); 41 | workbox.routing.registerRoute( 42 | /^https:\/\/cdnjs.cloudflare.com\//, 43 | workbox.strategies.networkFirst() 44 | ); 45 | workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst()); 46 | 47 | /** 48 | * Response to client after skipping waiting with MessageChannel 49 | */ 50 | addEventListener('message', event => { 51 | const replyPort = event.ports[0]; 52 | const message = event.data; 53 | if (replyPort && message && message.type === 'skip-waiting') { 54 | event.waitUntil( 55 | self 56 | .skipWaiting() 57 | .then( 58 | () => replyPort.postMessage({ error: null }), 59 | error => replyPort.postMessage({ error }) 60 | ) 61 | ); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /interactable.ts/dev-config/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 6 | 7 | const baseConfig = require('./webpack.config.base'); 8 | 9 | const config = { 10 | ...baseConfig, 11 | output: { 12 | ...baseConfig.output, 13 | filename: '[name].[chunkhash].js', 14 | chunkFilename: '[name].[chunkhash].chunk.js' 15 | }, 16 | module: { 17 | rules: [ 18 | ...baseConfig.module.rules.filter( 19 | rule => !['/\\.css$/', '/\\.less$/', '/\\.(scss|sass)$/'].includes(rule.test.toString()) 20 | ), 21 | { 22 | test: /\.css$/, 23 | use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] 24 | }, 25 | { 26 | test: /\.less$/, 27 | exclude: /antdTheme/, 28 | use: [ 29 | MiniCssExtractPlugin.loader, 30 | baseConfig.extra.moduleCSSLoader, 31 | 'postcss-loader', 32 | 'less-loader' 33 | ] 34 | }, 35 | { 36 | test: /\.(scss|sass)$/, 37 | use: [ 38 | MiniCssExtractPlugin.loader, 39 | baseConfig.extra.moduleCSSLoader, 40 | 'postcss-loader', 41 | 'sass-loader' 42 | ] 43 | } 44 | ] 45 | }, 46 | plugins: [ 47 | ...baseConfig.plugins, 48 | new MiniCssExtractPlugin({ 49 | filename: '[name].css' 50 | }), 51 | new webpack.DefinePlugin({ 52 | PRODUCTION: JSON.stringify(true) 53 | }) 54 | 55 | // 使用 Prepack 优化包体大小 56 | // 暂时存在 Bug,等待修复 57 | // 使用前 21 - 425 58 | // 使用后 21 - 433 59 | // new PrepackWebpackPlugin({ 60 | // mathRandomSeed: '0' 61 | // }), 62 | 63 | // new BundleAnalyzerPlugin({ 64 | // analyzerMode: 'static' 65 | // }) 66 | ], 67 | optimization: { 68 | runtimeChunk: 'single', 69 | minimizer: [new UglifyJsPlugin(), new OptimizeCSSAssetsPlugin({})] 70 | } 71 | }; 72 | 73 | delete config.extra; 74 | 75 | module.exports = config; 76 | -------------------------------------------------------------------------------- /interactable.ts/src/swiper/swiper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by apple on 16/7/1. 3 | */ 4 | import React, {PropTypes, Component} from "react"; 5 | import {ReactExternalLoader} from "../../external_loader/external_loader"; 6 | 7 | require("./swiper.scss"); 8 | 9 | /** 10 | * @function 默认的Swiper容器 11 | */ 12 | export class SwiperContainer extends Component { 13 | 14 | /** 15 | * @function 声明Props类型 16 | * @type {{options: *}} 17 | */ 18 | static propTypes = { 19 | options: PropTypes.object 20 | }; 21 | 22 | static defaultProps = { 23 | options: {} 24 | }; 25 | 26 | 27 | /** 28 | * @function 默认的构造函数 29 | */ 30 | constructor(props) { 31 | 32 | super(props); 33 | 34 | // 绑定私有方法的this指针 35 | this._onExternalLoaded = this._onExternalLoaded.bind(this); 36 | } 37 | 38 | /** 39 | * @function 在外部脚本加载完成后执行Swiper的渲染 40 | * @private 41 | */ 42 | _onExternalLoaded() { 43 | 44 | /** 45 | * @function 初始化Swiper 46 | * @type {Window.Swiper|Swiper} 47 | */ 48 | this.swiper = new Swiper('.swiper-container', this.props.options); 49 | } 50 | 51 | render() { 52 | return ( 53 | 59 |
60 |
61 | {this.props.children} 62 |
63 |
64 |
65 | ); 66 | } 67 | 68 | } 69 | 70 | 71 | /** 72 | * @function 默认的Swiper页帧 73 | */ 74 | export class SwiperSlide extends Component { 75 | 76 | /** 77 | * @function 默认渲染函数 78 | * @returns {XML} 79 | */ 80 | render() { 81 | 82 | return (
83 | {this.props.children} 84 |
); 85 | 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /interactable.ts/src/scalable-view/README.md: -------------------------------------------------------------------------------- 1 | # ScalableComponent 2 | 3 | > 借鉴了[pageResponse](https://github.com/peunzhang/pageResponse/blob/master/README.md)这个移动端响应式插件本项目的开发环境脚手架使用了[Webpack-React-Redux-Boilerplate](https://github.com/wxyyxc1992/Webpack-React-Redux-Boilerplate/tree/boilerplate) 4 | 5 | # Usage 6 | 7 | 使用`npm`命令下载安装该依赖: 8 | 9 | ``` 10 | npm i scalable-component --save 11 | ``` 12 | 13 | ## 设置视口 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | ## 导入 ScalableComponent 20 | 21 | ``` 22 | /** 23 | * Created by apple on 16/6/30. 24 | */ 25 | import React from "react"; 26 | import {render} from "react-dom"; 27 | import ScalableComponent from "./scalable"; 28 | 29 | render( 30 |
31 |

HI

32 |

This is Demo For Scalable

33 | 35 |
36 |
, document.getElementById('root')); 37 | ``` 38 | 39 | ths props of ScalableComponent is : 40 | 41 | ``` 42 | static propTypes = { 43 | mode: PropTypes.oneOf(['auto', 'contain', 'cover']), //modes 44 | width: PropTypes.number, //width of the visual Design 45 | height: PropTypes.number, //height of the visual Design 46 | origin: PropTypes.string,//origin for scale 47 | wrapperBackgroundColor: PropTypes.string//background Color for hatch area 48 | }; 49 | ``` 50 | 51 | # Mode 52 | 53 | ## Contain 54 | 55 | Contain 模式即保证页面完全包含在浏览器窗口中,在保证页面的宽高比情况下调整页面的宽度或者高度中的较大者,使得页面水平垂直居中,左右或上下可能出现空白。 56 | 57 | ![](https://user-images.githubusercontent.com/5803001/34641517-9804daa2-f340-11e7-8114-8413b91559d5.gif) 58 | 59 | ## Cover 60 | 61 | Cover 模式即使页面完全覆盖浏览器窗口,保持页面的宽高比,调整页面的宽度或高度(较小者),页面水平垂直居中,超出浏览器窗口左右或上下的内容会被隐藏 62 | ![](https://user-images.githubusercontent.com/5803001/34641518-9909a8c4-f340-11e7-8778-dd6dfe3f5538.gif) 63 | 64 | ## Auto 65 | 66 | 保持页面的宽高比,调整页面的宽度,使页面宽度完全包含在浏览器窗口中 67 | ![](https://user-images.githubusercontent.com/5803001/34641516-93ee7482-f340-11e7-87de-e6b979479036.gif) 68 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fc-gallery-react", 3 | "version": "0.0.6", 4 | "description": "fc-gallery-react", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/wx-chevalier/fractal-components" 8 | }, 9 | "license": "MIT", 10 | "main": "dist/cjs/index.js", 11 | "module": "dist/es/index.js", 12 | "types": "dist/types/index.d.ts", 13 | "keywords": [ 14 | "webpack", 15 | "react" 16 | ], 17 | "author": "wx-chevalier@github", 18 | "prettier": { 19 | "printWidth": 100, 20 | "singleQuote": true 21 | }, 22 | "lint-staged": { 23 | "*.{ts,tsx,scss,less,md}": [ 24 | "prettier --write", 25 | "git add" 26 | ] 27 | }, 28 | "scripts": { 29 | "build": "npm run build:es && npm run build:cjs", 30 | "build:es": "tsc --project ./tsconfig.es.json && npm run copy dist/es/", 31 | "build:cjs": "tsc --project ./tsconfig.cjs.json && npm run copy dist/cjs/", 32 | "clean": "rimraf dist", 33 | "copy": "copyfiles -u 1 './src/**/*.{less,svg}' ", 34 | "dev": "webpack-dev-server --config ./scripts/webpack/webpack.config.dev.js --hot", 35 | "start": "npm run dev", 36 | "test": "jest --config ./scripts/jest/jest.config.js", 37 | "test:watch": "npm test -- --watch", 38 | "test:cov": "npm run cleanCov && npm test -- --coverage" 39 | }, 40 | "dependencies": { 41 | "lodash.debounce": "^4.0.8", 42 | "lodash.throttle": "^4.1.1", 43 | "moment": "^2.24.0", 44 | "react": "^16.9.0", 45 | "react-dom": "^16.9.0", 46 | "react-image-lightbox": "^5.1.1", 47 | "react-sizeme": "^2.6.7", 48 | "react-swipeable": "^5.4.0", 49 | "react-tabs": "^3.0.0", 50 | "resize-observer-polyfill": "^1.5.1" 51 | }, 52 | "devDependencies": { 53 | "@typescript-eslint/eslint-plugin": "^2.0.0", 54 | "@typescript-eslint/parser": "^2.0.0", 55 | "copyfiles": "^2.1.1", 56 | "eslint": "^6.2.2", 57 | "eslint-config-airbnb": "^18.0.1", 58 | "eslint-config-prettier": "^6.1.0", 59 | "eslint-loader": "3.0.0", 60 | "eslint-plugin-import": "^2.18.2", 61 | "eslint-plugin-jsx-a11y": "^6.2.3", 62 | "eslint-plugin-prettier": "^3.1.0", 63 | "eslint-plugin-react": "^7.14.3", 64 | "prettier": "^1.18.2", 65 | "ts-jest": "^24.0.2", 66 | "typescript": "^3.5.3", 67 | "webpack": "^4.39.2", 68 | "webpack-dev-server": "^3.8.0", 69 | "webpack-merge": "^4.2.1" 70 | }, 71 | "files": [ 72 | "dist/" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /interactable.ts/src/scroll/pull-to-refresh/PullToRefresh.scss: -------------------------------------------------------------------------------- 1 | .ptr-element { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | color: #aaa; 7 | z-index: 10; 8 | text-align: center; 9 | height: 50px; 10 | } 11 | 12 | .ptr-element .genericon { 13 | opacity: .6; 14 | font-size: 34px; 15 | width: auto; 16 | height: auto; 17 | transition: all .25s ease; 18 | -webkit-transform: rotate(90deg); 19 | transform: rotate(90deg); 20 | margin-top: 5px; 21 | } 22 | .ptr-refresh .ptr-element .genericon { 23 | -webkit-transform: rotate(270deg); 24 | transform: rotate(270deg); 25 | } 26 | .ptr-loading .ptr-element .genericon, 27 | .ptr-reset .ptr-element .genericon { 28 | display: none; 29 | } 30 | 31 | .loading { 32 | display: inline-block; 33 | text-align: center; 34 | opacity: .4; 35 | margin: 12px 0 0 5px; 36 | display: none; 37 | } 38 | .ptr-loading .loading { 39 | display: block; 40 | } 41 | 42 | .loading span { 43 | display: inline-block; 44 | vertical-align: middle; 45 | width: 10px; 46 | height: 10px; 47 | margin-right: 3px; 48 | -webkit-transform: scale(0.3); 49 | transform: scale(0.3); 50 | border-radius: 50%; 51 | -webkit-animation: ptr-loading 0.4s infinite alternate; 52 | animation: ptr-loading 0.4s infinite alternate; 53 | } 54 | 55 | .loading-ptr-1 { 56 | -webkit-animation-delay: 0; 57 | animation-delay: 0 !important; 58 | } 59 | 60 | .loading-ptr-2 { 61 | -webkit-animation-delay: 0.2s; 62 | animation-delay: 0.2s !important; 63 | } 64 | 65 | .loading-ptr-3 { 66 | -webkit-animation-delay: 0.4s; 67 | animation-delay: 0.4s !important; 68 | } 69 | 70 | @-webkit-keyframes ptr-loading { 71 | 0% { 72 | -webkit-transform: translateY(0) scale(0.3); 73 | transform: translateY(0) scale(0.3); 74 | opacity: 0; 75 | } 76 | 77 | 100% { 78 | -webkit-transform: scale(1); 79 | transform: scale(1); 80 | background-color: #333; 81 | opacity: 1; 82 | } 83 | } 84 | 85 | @keyframes ptr-loading { 86 | 0% { 87 | -webkit-transform: translateY(0) scale(0.3); 88 | transform: translateY(0) scale(0.3); 89 | opacity: 0; 90 | } 91 | 92 | 100% { 93 | -webkit-transform: scale(1); 94 | transform: scale(1); 95 | background-color: #333; 96 | opacity: 1; 97 | } 98 | } 99 | 100 | .ptr-loading .refresh-view, 101 | .ptr-reset .refresh-view, 102 | .ptr-loading .ptr-element, 103 | .ptr-reset .ptr-element { 104 | transition: all .25s ease; 105 | } 106 | 107 | .ptr-reset .refresh-view { 108 | -webkit-transform: translate3d(0, 0, 0); 109 | transform: translate3d(0, 0, 0); 110 | } 111 | 112 | .ptr-loading .refresh-view { 113 | -webkit-transform: translate3d(0, 30px, 0); 114 | transform: translate3d(0, 30px, 0); 115 | } 116 | -------------------------------------------------------------------------------- /interactable.ts/src/scalable-view/README.en.md: -------------------------------------------------------------------------------- 1 | # [ScalableComponent](https://github.com/wxyyxc1992/Webpack-React-Redux-Boilerplate/tree/master/widgets/components/scalable) 2 | 3 | > Inspired By [pageResponse](https://github.com/peunzhang/pageResponse/blob/master/README.md), which is a Responsive plugin for the mobile 4 | 5 | 6 | ScalableComponent is based on the CSS3 transform property: scale, it will do auto scale in different devices while abiding by the aspect ratio of visual design 7 | 8 | # Usage 9 | 10 | ## Set Viewport to disable device scale 11 | ``` 12 | 13 | ``` 14 | 15 | ## Import ScalableComponent 16 | 17 | ``` 18 | /** 19 | * Created by apple on 16/6/30. 20 | */ 21 | import React from "react"; 22 | import {render} from "react-dom"; 23 | import ScalableComponent from "./scalable"; 24 | 25 | render( 26 |
27 |

HI

28 |

This is Demo For Scalable

29 | 31 |
32 |
, document.getElementById('root')); 33 | 34 | ``` 35 | 36 | ths props of ScalableComponent is : 37 | 38 | ``` 39 | static propTypes = { 40 | mode: PropTypes.oneOf(['auto', 'contain', 'cover']), //modes 41 | width: PropTypes.number, //width of the visual Design 42 | height: PropTypes.number, //height of the visual Design 43 | origin: PropTypes.string,//origin for scale 44 | wrapperBackgroundColor: PropTypes.string//background Color for hatch area 45 | }; 46 | 47 | ``` 48 | 49 | 50 | # Mode 51 | 52 | ## Contain 53 | 54 | Contain mode ensure that page is wrapped in the window, there may be some blank area in left/right/top/bottom area. 55 | 56 | ![](https://github.com/wxyyxc1992/Webpack-React-Redux-Boilerplate/blob/master/widgets/components/scalable/screenshots/contain.gif?raw=true) 57 | 58 | ## Cover 59 | 60 | Cover mode ensure that the page cover the window while keep the aspect ratio. 61 | 62 | ![](https://github.com/wxyyxc1992/Webpack-React-Redux-Boilerplate/blob/master/widgets/components/scalable/screenshots/cover.gif?raw=true) 63 | 64 | ## Auto 65 | 66 | keep the aspect ratio and adjust the width, ensure that page is wrapped in window in vertical direction 67 | 68 | ![](https://github.com/wxyyxc1992/Webpack-React-Redux-Boilerplate/blob/master/widgets/components/scalable/screenshots/auto.gif?raw=true) 69 | 70 | # Issue or Advice 71 | 72 | welcome to open issue, this component is in development so you can propose any thing you want to be covered in it. 73 | 74 | -------------------------------------------------------------------------------- /interactable.ts/src/scroll/pull-to-refresh/PullToRefresh.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import WebPullToRefresh from './wptr.js'; 3 | import './PullToRefresh.scss'; 4 | 5 | /** 6 | * Description 下拉刷新界面 7 | */ 8 | export default class PullToRefresh extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | initialized: false 13 | }; 14 | this.handleRefresh = this.handleRefresh.bind(this); 15 | } 16 | 17 | handleRefresh() { 18 | return new Promise((resolve, reject) => { 19 | this.props.onRefresh(resolve, reject); 20 | }); 21 | } 22 | 23 | init() { 24 | if (!this.state.initialized) { 25 | WebPullToRefresh().init({ 26 | contentEl: this.$refresh, 27 | ptrEl: this.$ptr, 28 | bodyEl: this.$body, 29 | distanceToRefresh: this.props.distanceToRefresh || undefined, 30 | loadingFunction: this.handleRefresh, 31 | resistance: this.props.resistance || undefined, 32 | hammerOptions: this.props.hammerOptions || undefined 33 | }); 34 | this.setState({ 35 | initialized: true 36 | }); 37 | } 38 | } 39 | 40 | componentDidMount() { 41 | if (!this.props.disabled) { 42 | this.init(); 43 | } 44 | } 45 | 46 | componentDidUpdate() { 47 | if (!this.props.disabled) { 48 | this.init(); 49 | } 50 | } 51 | 52 | render() { 53 | const { 54 | children, 55 | disabled, 56 | distanceToRefresh, 57 | hammerOptions, 58 | icon, 59 | loading, 60 | onRefresh, 61 | resistance, 62 | ...rest 63 | } = this.props; 64 | 65 | if (disabled) { 66 | return ( 67 |
68 | {children} 69 |
70 | ); 71 | } 72 | 73 | return ( 74 |
{ 76 | this.$body = $body; 77 | this.props.onMount($body); 78 | }} 79 | {...rest} 80 | > 81 |
{ 83 | this.$ptr = $ptr; 84 | }} 85 | className="ptr-element" 86 | > 87 | {icon || } 88 | {loading || 89 |
90 | 91 | 92 | 93 |
} 94 |
95 |
{ 97 | this.$refresh = $refresh; 98 | }} 99 | className="refresh-view" 100 | > 101 | {children} 102 |
103 |
104 | ); 105 | } 106 | } 107 | 108 | PullToRefresh.propTypes = { 109 | onRefresh: PropTypes.func.isRequired, 110 | onMount: PropTypes.func, 111 | icon: PropTypes.element, 112 | loading: PropTypes.element, 113 | disabled: PropTypes.bool, 114 | className: PropTypes.string, 115 | style: PropTypes.object, 116 | distanceToRefresh: PropTypes.number, 117 | resistance: PropTypes.number, 118 | hammerOptions: PropTypes.object 119 | }; 120 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook, act, RenderHookResult } from '@testing-library/react-hooks'; 2 | import { DependencyList } from 'react'; 3 | import useVirtualList, { OptionType } from '../index'; 4 | 5 | /* 暂时关闭 act 警告 见:https://github.com/testing-library/react-testing-library/issues/281#issuecomment-480349256 */ 6 | const originalError = console.error; 7 | beforeAll(() => { 8 | console.error = (...args: any) => { 9 | if (/Warning.*not wrapped in act/.test(args[0])) { 10 | return; 11 | } 12 | originalError.call(console, ...args); 13 | }; 14 | }); 15 | 16 | afterAll(() => { 17 | console.error = originalError; 18 | }); 19 | 20 | describe('useVirtualList', () => { 21 | it('should be defined', () => { 22 | expect(useVirtualList).toBeDefined(); 23 | }); 24 | 25 | describe('virtual list render', () => { 26 | let mockRef = { scrollTop: 0, clientHeight: 300 }; 27 | let hook: RenderHookResult< 28 | { list: unknown[]; options: OptionType }, 29 | { 30 | list: unknown[]; 31 | scrollTo: (index: number) => void; 32 | containerProps: { 33 | ref: (ref: any) => void; 34 | }; 35 | wrapperProps: { 36 | style: { 37 | paddingTop: number; 38 | height: number; 39 | }; 40 | }; 41 | } 42 | >; 43 | 44 | const setup = (list: any[] = [], options: {}) => { 45 | hook = renderHook(() => 46 | useVirtualList(list as unknown[], { itemHeight: 30, ...options } as OptionType), 47 | ); 48 | hook.result.current.containerProps.ref(mockRef); 49 | }; 50 | 51 | afterEach(() => { 52 | hook.unmount(); 53 | mockRef = { scrollTop: 0, clientHeight: 300 }; 54 | }); 55 | 56 | it('test return list size', () => { 57 | setup(Array.from(Array(99999).keys()), {}); 58 | 59 | act(() => { 60 | hook.result.current.scrollTo(80); 61 | }); 62 | 63 | // 10 items plus 5 overscan * 2 64 | expect(hook.result.current.list.length).toBe(20); 65 | expect(mockRef.scrollTop).toBe(80 * 30); 66 | }); 67 | 68 | it('test with fixed height', () => { 69 | setup(Array.from(Array(99999).keys()), { overscan: 0 }); 70 | 71 | act(() => { 72 | hook.result.current.scrollTo(20); 73 | }); 74 | 75 | expect(hook.result.current.list.length).toBe(10); 76 | expect(mockRef.scrollTop).toBe(20 * 30); 77 | }); 78 | 79 | it('test with dynamic height', () => { 80 | setup(Array.from(Array(99999).keys()), { 81 | overscan: 0, 82 | itemHeight: (i: number) => (i % 2 === 0 ? 30 : 60), 83 | }); 84 | 85 | act(() => { 86 | hook.result.current.scrollTo(20); 87 | }); 88 | 89 | // average height for easy calculation 90 | const averageHeight = (30 + 60) / 2; 91 | 92 | expect(hook.result.current.list.length).toBe(Math.floor(300 / averageHeight)); 93 | expect(mockRef.scrollTop).toBe(10 * 30 + 10 * 60); 94 | expect((hook.result.current.list[0] as { data: number }).data).toBe(20); 95 | expect((hook.result.current.list[0] as { index: number }).index).toBe(20); 96 | expect((hook.result.current.list[5] as { data: number }).data).toBe(25); 97 | expect((hook.result.current.list[5] as { index: number }).index).toBe(25); 98 | 99 | expect(hook.result.current.wrapperProps.style.paddingTop).toBe(20 * averageHeight); 100 | expect(hook.result.current.wrapperProps.style.height).toBe(99998 * averageHeight + 30); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /interactable.ts/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type ResizableBoxDirection = 4 | | 'top' 5 | | 'right' 6 | | 'bottom' 7 | | 'left' 8 | | 'topRight' 9 | | 'bottomRight' 10 | | 'bottomLeft' 11 | | 'topLeft'; 12 | 13 | export interface ResizableBoxState { 14 | width: number | string; 15 | height: number | string; 16 | direction?: ResizableBoxDirection; 17 | original?: { 18 | x: number; 19 | y: number; 20 | width: number; 21 | height: number; 22 | }; 23 | isResizing?: boolean; 24 | resizeCursor: string; 25 | } 26 | 27 | export type NumberSize = { 28 | width: number; 29 | height: number; 30 | }; 31 | 32 | export type Size = { 33 | width: string | number; 34 | height: string | number; 35 | }; 36 | 37 | export type CSSSize = { 38 | width: string; 39 | height: string; 40 | }; 41 | 42 | export type HandleComponent = { 43 | top?: React.ReactNode; 44 | right?: React.ReactNode; 45 | bottom?: React.ReactNode; 46 | left?: React.ReactNode; 47 | topRight?: React.ReactNode; 48 | bottomRight?: React.ReactNode; 49 | bottomLeft?: React.ReactNode; 50 | topLeft?: React.ReactNode; 51 | }; 52 | 53 | export type ResizeCallback = ( 54 | event: MouseEvent | TouchEvent, 55 | direction: ResizableBoxDirection, 56 | elementRef: HTMLDivElement, 57 | delta: NumberSize 58 | ) => void; 59 | 60 | export type ResizeStartCallback = ( 61 | e: React.MouseEvent | React.TouchEvent, 62 | dir: ResizableBoxDirection, 63 | elementRef: HTMLDivElement, 64 | delta: NumberSize 65 | ) => void; 66 | 67 | export interface ResizableBoxProps extends React.HTMLAttributes { 68 | onResizeStart?: ResizeStartCallback; 69 | onResize?: ResizeCallback; 70 | onResizeStop?: ResizeCallback; 71 | style?: React.CSSProperties; 72 | handleStyles?: { 73 | top?: React.CSSProperties; 74 | right?: React.CSSProperties; 75 | bottom?: React.CSSProperties; 76 | left?: React.CSSProperties; 77 | topRight?: React.CSSProperties; 78 | bottomRight?: React.CSSProperties; 79 | bottomLeft?: React.CSSProperties; 80 | topLeft?: React.CSSProperties; 81 | }; 82 | handleClasses?: { 83 | top?: string; 84 | right?: string; 85 | bottom?: string; 86 | left?: string; 87 | topRight?: string; 88 | bottomRight?: string; 89 | bottomLeft?: string; 90 | topLeft?: string; 91 | }; 92 | enable?: { 93 | top?: boolean; 94 | right?: boolean; 95 | bottom?: boolean; 96 | left?: boolean; 97 | topRight?: boolean; 98 | bottomRight?: boolean; 99 | bottomLeft?: boolean; 100 | topLeft?: boolean; 101 | }; 102 | className?: string; 103 | defaultSize?: { 104 | width?: string | number; 105 | height?: string | number; 106 | }; 107 | size?: { 108 | width?: string | number; 109 | height?: string | number; 110 | }; 111 | minWidth?: number | string; 112 | minHeight?: number | string; 113 | maxWidth?: number | string; 114 | maxHeight?: number | string; 115 | grid?: number[]; 116 | snap?: { 117 | x?: number[]; 118 | y?: number[]; 119 | }; 120 | bounds?: 'parent' | 'window' | HTMLElement; 121 | lockAspectRatio?: boolean | number; 122 | lockAspectRatioExtraWidth?: number; 123 | lockAspectRatioExtraHeight?: number; 124 | handleWrapperStyle?: React.CSSProperties; 125 | handleWrapperClass?: string; 126 | handleComponent?: HandleComponent; 127 | } 128 | 129 | export default class ResizableBox extends React.Component { 130 | ResizableBox: HTMLElement; 131 | 132 | size: { 133 | width: number; 134 | height: number; 135 | }; 136 | 137 | updateSize({ width, height }: Size): void; 138 | } 139 | -------------------------------------------------------------------------------- /gallery.ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fc-gallery-workspace", 3 | "version": "0.0.1", 4 | "description": "A React Schedule component with virtual rendering", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/wx-chevalier/fractch-components" 8 | }, 9 | "license": "MIT", 10 | "keywords": [ 11 | "react", 12 | "redux", 13 | "mobx", 14 | "webpack", 15 | "typescript" 16 | ], 17 | "author": "wx-chevalier@github", 18 | "private": true, 19 | "workspaces": [ 20 | "packages/fc-gallery-core", 21 | "packages/fc-gallery-react" 22 | ], 23 | "husky": { 24 | "hooks": { 25 | "pre-commit": "npm run lint-staged" 26 | } 27 | }, 28 | "prettier": { 29 | "printWidth": 100, 30 | "singleQuote": true 31 | }, 32 | "lint-staged": { 33 | "*.{ts,tsx,scss,less,md}": [ 34 | "prettier --write", 35 | "git add" 36 | ] 37 | }, 38 | "browserslist": [ 39 | "> 1%", 40 | "last 2 versions", 41 | "not ie <= 10" 42 | ], 43 | "scripts": { 44 | "postinstall": "node ./node_modules/husky/lib/installer/bin install", 45 | "bootstrap": "yarn install && yarn run build", 46 | "build": "npm run clean && yarn workspaces run build", 47 | "clean": "yarn workspaces run clean", 48 | "clean:cov": "yarn workspaces run clean:cov", 49 | "dev": "npm start", 50 | "lint": "yarn workspaces run lint", 51 | "lint-staged": "lint-staged", 52 | "prettier-all": "prettier --write 'src/**/*' '!src/{assets,datas}/**'", 53 | "start": "(cd packages/fc-gallery-react && npm start)", 54 | "start:m-fe": "./scripts/tools/start_micro.sh", 55 | "test": "yarn workspaces run test", 56 | "test:watch": "yarn workspaces run test:watch", 57 | "test:cov": "yarn workspaces run test:cov", 58 | "upgrade": "./scripts/tools/upgrade_pkgs.sh" 59 | }, 60 | "devDependencies": { 61 | "@svgr/webpack": "^4.3.2", 62 | "@types/jest": "24.0.18", 63 | "@types/react-dom": "^16.9.0", 64 | "@typescript-eslint/eslint-plugin": "^2.0.0", 65 | "@typescript-eslint/parser": "^2.0.0", 66 | "autoprefixer": "9.6.1", 67 | "awesome-typescript-loader": "^5.2.1", 68 | "copy-webpack-plugin": "^5.0.4", 69 | "css-loader": "3.2.0", 70 | "enzyme": "^3.10.0", 71 | "enzyme-adapter-react-16": "^1.14.0", 72 | "enzyme-to-json": "^3.4.0", 73 | "eslint": "^6.2.2", 74 | "eslint-loader": "3.0.0", 75 | "eslint-plugin-import": "^2.18.2", 76 | "eslint-plugin-jsx-a11y": "^6.2.3", 77 | "eslint-plugin-prettier": "^3.1.0", 78 | "eslint-plugin-react": "^7.14.3", 79 | "file-loader": "4.2.0", 80 | "fork-ts-checker-webpack-plugin": "^1.5.0", 81 | "html-webpack-plugin": "^3.2.0", 82 | "html-webpack-template": "^6.2.0", 83 | "husky": "^3.0.4", 84 | "jest": "24.9.0", 85 | "jest-cli": "24.9.0", 86 | "jest-fetch-mock": "^2.1.2", 87 | "json-server": "0.15.0", 88 | "less": "^3.10.3", 89 | "less-loader": "^5.0.0", 90 | "lint-staged": "^9.2.5", 91 | "mini-css-extract-plugin": "^0.8.0", 92 | "optimize-css-assets-webpack-plugin": "5.0.3", 93 | "parallelshell": "^3.0.2", 94 | "postcss-loader": "3.0.0", 95 | "react-hot-loader": "^4.12.11", 96 | "resolve-url-loader": "3.1.0", 97 | "rimraf": "^3.0.0", 98 | "style-loader": "1.0.0", 99 | "terser-webpack-plugin": "^1.4.1", 100 | "ts-import-plugin": "^1.6.1", 101 | "ts-jest": "^24.0.2", 102 | "ts-loader": "6.0.4", 103 | "tsconfig-paths-webpack-plugin": "^3.2.0", 104 | "typescript": "3.5.3", 105 | "uglifyjs-webpack-plugin": "2.2.0", 106 | "url-loader": "^2.1.0", 107 | "wasm-loader": "^1.3.0", 108 | "webpack": "^4.39.2", 109 | "webpack-cli": "3.3.7", 110 | "webpack-dashboard": "3.0.7", 111 | "webpack-dev-server": "^3.8.0", 112 | "webpack-theme-color-replacer": "^1.2.17", 113 | "workerize-loader": "^1.1.0" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/example/pages/App.less: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | background: #f4f6f9; 5 | color: #222; 6 | font-family: 'Raleway', sans-serif; 7 | } 8 | 9 | ul, 10 | li { 11 | padding: 0; 12 | margin: 0; 13 | list-style: none; 14 | } 15 | 16 | li { 17 | padding: 3px 0; 18 | display: inline-block; 19 | } 20 | 21 | label { 22 | margin-left: 5px; 23 | } 24 | 25 | .app { 26 | } 27 | 28 | .app-header { 29 | letter-spacing: 1px; 30 | text-transform: uppercase; 31 | } 32 | 33 | .play-button { 34 | cursor: pointer; 35 | position: absolute; 36 | left: 0; 37 | top: 0; 38 | bottom: 0; 39 | right: 0; 40 | margin: auto; 41 | height: 60px; 42 | width: 100px; 43 | background-color: rgba(0, 0, 0, 0.7); 44 | border-radius: 5px; 45 | } 46 | 47 | .play-button:hover { 48 | background-color: rgba(0, 0, 0, 0.9); 49 | } 50 | 51 | .play-button:after { 52 | content: ''; 53 | display: block; 54 | position: absolute; 55 | top: 16.5px; 56 | left: 40px; 57 | margin: 0 auto; 58 | border-style: solid; 59 | border-width: 12.5px 0 12.5px 20px; 60 | border-color: transparent transparent transparent rgba(255, 255, 255, 1); 61 | } 62 | 63 | .close-video::before { 64 | content: '✖'; 65 | cursor: pointer; 66 | position: absolute; 67 | right: 0; 68 | top: 0; 69 | font-size: 20px; 70 | padding: 20px; 71 | z-index: 1; 72 | line-height: 0.7; 73 | display: block; 74 | color: #fff; 75 | } 76 | 77 | .video-wrapper { 78 | position: relative; 79 | padding: 33.35% 0; /* 16:9 */ 80 | height: 0; 81 | } 82 | 83 | .video-wrapper iframe { 84 | position: absolute; 85 | top: 0; 86 | left: 0; 87 | width: 100%; 88 | height: 100%; 89 | } 90 | 91 | .app .image-gallery, 92 | .app-sandbox { 93 | margin: 0 auto; 94 | width: 65%; 95 | transition: all 1s ease; 96 | } 97 | 98 | @media (max-width: 1320px) { 99 | .app-sandbox-content { 100 | padding: 0 20px; 101 | } 102 | } 103 | 104 | .app-sandbox { 105 | margin: 40px auto; 106 | -webkit-user-select: none; 107 | -moz-user-select: none; 108 | -ms-user-select: none; 109 | -o-user-select: none; 110 | user-select: none; 111 | } 112 | 113 | .app-buttons li { 114 | display: block; 115 | } 116 | 117 | @media (max-width: 768px) { 118 | .app-header { 119 | font-size: 20px; 120 | } 121 | 122 | .app-buttons li { 123 | display: block; 124 | margin: 10px 0; 125 | } 126 | 127 | .app-buttons li + li { 128 | padding: 0; 129 | } 130 | 131 | .play-button { 132 | height: 40px; 133 | width: 65px; 134 | } 135 | 136 | .play-button:after { 137 | top: 11px; 138 | left: 27px; 139 | border-width: 8.5px 0 8.5px 12px; 140 | } 141 | 142 | .close-video::before { 143 | font-size: 16px; 144 | padding: 15px; 145 | } 146 | } 147 | 148 | @media (max-width: 1024px) { 149 | .app .image-gallery, 150 | .app-sandbox { 151 | width: 100%; 152 | } 153 | } 154 | 155 | .app-interval-input-group { 156 | display: table; 157 | } 158 | 159 | .app-interval-label { 160 | display: table-cell; 161 | vertical-align: middle; 162 | padding: 6px 12px; 163 | font-size: 14px; 164 | font-weight: 400; 165 | line-height: 1; 166 | color: #555; 167 | text-align: center; 168 | background-color: #eee; 169 | border: 3px solid #ccc; 170 | border-right: none; 171 | border-radius: 4px; 172 | border-top-right-radius: 0; 173 | border-bottom-right-radius: 0; 174 | } 175 | 176 | .app-interval-input { 177 | -webkit-appearance: none; 178 | display: table-cell; 179 | margin: 0; 180 | padding: 9px; 181 | border-radius: 5px; 182 | font-size: 14px; 183 | border: 3px solid #ccc; 184 | background: #fff; 185 | width: 100%; 186 | border-top-left-radius: 0; 187 | border-bottom-left-radius: 0; 188 | } 189 | 190 | input.app-interval-input { 191 | width: 65px; 192 | } 193 | 194 | .app-checkboxes { 195 | margin-top: 10px; 196 | } 197 | 198 | .app-checkboxes li { 199 | display: block; 200 | } 201 | -------------------------------------------------------------------------------- /micro-virtual-view/packages/react-virtual-view/src/hooks/useVirtualList/index.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useMemo } from 'react'; 2 | import useSize from '../useSize'; 3 | 4 | export interface OptionType { 5 | itemHeight: number | ((index: number) => number); 6 | overscan?: number; 7 | } 8 | 9 | export default (list: T[], options: OptionType) => { 10 | const [size, containerRef] = useSize(); 11 | // 暂时禁止 cache 12 | // const distanceCache = useRef<{ [key: number]: number }>({}); 13 | const [state, setState] = useState({ start: 0, end: 10 }); 14 | const { itemHeight, overscan = 5 } = options; 15 | 16 | if (!itemHeight) { 17 | console.warn('please enter a valid itemHeight'); 18 | } 19 | 20 | const getViewCapacity = (containerHeight: number) => { 21 | if (typeof itemHeight === 'number') { 22 | return Math.ceil(containerHeight / itemHeight); 23 | } 24 | const { start = 0 } = state; 25 | let sum = 0; 26 | let capacity = 0; 27 | for (let i = start; i < list.length; i++) { 28 | const height = (itemHeight as ((index: number) => number))(i); 29 | sum += height; 30 | if (sum >= containerHeight) { 31 | capacity = i; 32 | break; 33 | } 34 | } 35 | return capacity - start; 36 | }; 37 | 38 | const getOffset = (scrollTop: number) => { 39 | if (typeof itemHeight === 'number') { 40 | return Math.floor(scrollTop / itemHeight) + 1; 41 | } 42 | let sum = 0; 43 | let offset = 0; 44 | for (let i = 0; i < list.length; i++) { 45 | const height = (itemHeight as ((index: number) => number))(i); 46 | sum += height; 47 | if (sum >= scrollTop) { 48 | offset = i; 49 | break; 50 | } 51 | } 52 | return offset + 1; 53 | }; 54 | 55 | const calculateRange = () => { 56 | const element = containerRef.current; 57 | if (element) { 58 | const offset = getOffset(element.scrollTop); 59 | const viewCapacity = getViewCapacity(element.clientHeight); 60 | 61 | const from = offset - overscan; 62 | const to = offset + viewCapacity + overscan; 63 | setState({ start: from < 0 ? 0 : from, end: to > list.length ? list.length : to }); 64 | } 65 | }; 66 | 67 | useEffect(() => { 68 | calculateRange(); 69 | }, [size.width, size.height]); 70 | 71 | const totalHeight = useMemo(() => { 72 | if (typeof itemHeight === 'number') { 73 | return list.length * itemHeight; 74 | } 75 | return list.reduce((sum, _, index) => sum + itemHeight(index), 0); 76 | }, [list.length]); 77 | 78 | const getDistanceTop = (index: number) => { 79 | // 如果有缓存,优先返回缓存值 80 | // if (enableCache && distanceCache.current[index]) { 81 | // return distanceCache.current[index]; 82 | // } 83 | if (typeof itemHeight === 'number') { 84 | const height = index * itemHeight; 85 | // if (enableCache) { 86 | // distanceCache.current[index] = height; 87 | // } 88 | return height; 89 | } 90 | const height = list.slice(0, index).reduce((sum, _, i) => sum + itemHeight(i), 0); 91 | // if (enableCache) { 92 | // distanceCache.current[index] = height; 93 | // } 94 | return height; 95 | }; 96 | 97 | const scrollTo = (index: number) => { 98 | if (containerRef.current) { 99 | containerRef.current.scrollTop = getDistanceTop(index); 100 | calculateRange(); 101 | } 102 | }; 103 | 104 | return { 105 | list: list.slice(state.start, state.end).map((ele, index) => ({ 106 | data: ele, 107 | index: index + state.start, 108 | })), 109 | scrollTo, 110 | containerProps: { 111 | ref: (ele: any) => { 112 | containerRef.current = ele; 113 | }, 114 | onScroll: (e: any) => { 115 | e.preventDefault(); 116 | calculateRange(); 117 | }, 118 | style: { overflowY: 'auto' as const }, 119 | }, 120 | wrapperProps: { 121 | style: { width: '100%', height: totalHeight, paddingTop: getDistanceTop(state.start) }, 122 | }, 123 | }; 124 | }; 125 | -------------------------------------------------------------------------------- /interactable.ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wx-webpack-react-umd", 3 | "version": "0.0.1", 4 | "description": "Boilerplate for webpack & react", 5 | "main": "./build/index.umd.js", 6 | "module": "./dist/index.umd.js", 7 | "jsnext:main": "./dist/index.umd.js", 8 | "typings": "./index.d.ts", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/wxyyxc1992/fe-boilerplate" 12 | }, 13 | "license": "MIT", 14 | "keywords": [ 15 | "webpack", 16 | "react" 17 | ], 18 | "author": "wxyyxc1992@github", 19 | "jest": { 20 | "moduleFileExtensions": [ 21 | "js", 22 | "jsx" 23 | ], 24 | "moduleDirectories": [ 25 | "node_modules", 26 | "bower_components", 27 | "shared" 28 | ], 29 | "moduleNameMapper": { 30 | "\\.(css|less|scss)$": "/dev-config/mock/styleMock.js", 31 | "\\.(gif|ttf|eot|svg)$": "/dev-config/mock/fileMock.js" 32 | } 33 | }, 34 | "prettier": { 35 | "printWidth": 100, 36 | "singleQuote": true 37 | }, 38 | "lint-staged": { 39 | "*.{ts,tsx,scss,less,md}": [ 40 | "prettier --write", 41 | "git add" 42 | ] 43 | }, 44 | "scripts": { 45 | "start": "webpack-dashboard -- npm run dev:node", 46 | "dev": "parallelshell \"npm run mock\" \"npm start\"", 47 | "dev:node": "webpack-serve --config ./dev-config/webpack.config.dev.js", 48 | "build": "parallelshell \"npm run build:umd\" \"npm run build:babel\"", 49 | "build:babel": "rm -rf dist && cross-env NODE_ENV=es-production BABEL_ENV=es-production node_modules/babel-cli/bin/babel.js src --out-dir dist", 50 | "build:dev": "npm run clean && webpack --config ./dev-config/webpack.config.prod.js --mode development", 51 | "build:umd": "npm run clean && BABEL_ENV=production webpack --config ./dev-config/webpack.config.umd.js --mode production", 52 | "stats": "webpack --config ./dev-config/webpack.config.prod.js --mode production --profile --json > build/stats.json", 53 | "analyze": "npm run stats && webpack-bundle-analyzer build/stats.json", 54 | "clean": "rimraf build && mkdir build", 55 | "test": "jest", 56 | "update": "npm-check -u", 57 | "lint": "eslint --ext .js,.jsx src", 58 | "mock": "json-server --watch dev-config/mock/db.json --port 9091", 59 | "storybook": "start-storybook -p 6006", 60 | "build-storybook": "build-storybook", 61 | "precommit": "lint-staged", 62 | "prettier-all": "prettier --write 'src/**/*' '!src/{assets,datas}/**'" 63 | }, 64 | "dependencies": { 65 | "antd": "^3.1.0", 66 | "babel-polyfill": "^6.26.0", 67 | "class-names": "^1.0.0", 68 | "moment": "^2.20.1", 69 | "orgchart": "^2.1.2", 70 | "prop-types": "^15.6.1", 71 | "react": "^16.2.0", 72 | "react-dom": "^16.2.0", 73 | "styled-components": "^3.2.6" 74 | }, 75 | "devDependencies": { 76 | "autoprefixer": "9.1.3", 77 | "babel-cli": "^6.26.0", 78 | "babel-core": "^6.26.0", 79 | "babel-eslint": "9.0.0", 80 | "babel-jest": "23.4.2", 81 | "babel-loader": "7.1.4", 82 | "babel-plugin-import": "^1.6.2", 83 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 84 | "babel-plugin-transform-react-remove-prop-types": "^0.4.13", 85 | "babel-preset-env": "^1.6.1", 86 | "babel-preset-react": "^6.24.1", 87 | "babel-preset-react-optimize": "^1.0.1", 88 | "babel-preset-stage-1": "^6.24.1", 89 | "babel-runtime": "^6.26.0", 90 | "copy-webpack-plugin": "^4.5.2", 91 | "cross-env": "^5.2.0", 92 | "css-loader": "1.0.0", 93 | "enzyme": "^3.3.0", 94 | "eslint": "5.5.0", 95 | "eslint-config-airbnb": "17.1.0", 96 | "eslint-config-prettier": "3.0.1", 97 | "eslint-loader": "^2.0.0", 98 | "eslint-plugin-import": "^2.11.0", 99 | "eslint-plugin-jest": "^21.15.0", 100 | "eslint-plugin-jsx-a11y": "^6.0.3", 101 | "eslint-plugin-react": "^7.7.0", 102 | "file-loader": "2.0.0", 103 | "html-webpack-plugin": "^3.1.0", 104 | "html-webpack-template": "^6.1.0", 105 | "jest": "23.5.0", 106 | "jest-cli": "23.5.0", 107 | "json-server": "0.14.0", 108 | "less": "^3.0.1", 109 | "less-loader": "^4.0.5", 110 | "mini-css-extract-plugin": "^0.4.0", 111 | "node-sass": "^4.7.2", 112 | "npm-check": "^5.6.0", 113 | "offline-plugin": "5.0.5", 114 | "optimize-css-assets-webpack-plugin": "5.0.1", 115 | "parallelshell": "3.0.1", 116 | "postcss-loader": "3.0.0", 117 | "react-hot-loader": "^4.0.1", 118 | "regenerator-runtime": "0.12.1", 119 | "resolve-url-loader": "^2.3.0", 120 | "rimraf": "^2.6.2", 121 | "sass-loader": "7.1.0", 122 | "style-loader": "0.23.0", 123 | "uglifyjs-webpack-plugin": "^1.2.7", 124 | "url-loader": "^1.0.1", 125 | "wasm-loader": "^1.3.0", 126 | "webpack": "4.17.1", 127 | "webpack-cli": "3.1.0", 128 | "webpack-dashboard": "2.0.0", 129 | "webpack-dev-server": "^3.1.11", 130 | "webpack-serve": "^2.0.2" 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /interactable.ts/src/scalable-view/ScalableView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by apple on 16/6/30. 3 | */ 4 | import React, { Component, PropTypes } from "react"; 5 | 6 | export default class ScalableView extends Component { 7 | // 设置属性值 8 | static propTypes = { 9 | mode: PropTypes.oneOf(["auto", "contain", "cover"]), // 选择的模式 10 | width: PropTypes.number, // 视觉稿宽度 11 | height: PropTypes.number, // 视觉稿高度 12 | origin: PropTypes.string, // 缩放中心点 13 | wrapperBackgroundColor: PropTypes.string // 背景颜色 14 | }; 15 | 16 | /** 17 | * @function 构造函数 18 | * @param props 19 | */ 20 | constructor(props) { 21 | super(props); 22 | 23 | const userAgent = navigator.userAgent; 24 | 25 | // 判断是否为指定设备 26 | this.wp = userAgent.match(/Windows Phone ([\d.]+)/); // 判断是否为WindowsPhone 27 | this.android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/); // 判断是否为Android 28 | 29 | // 获取设备的宽高比 30 | this.state = { 31 | deviceWidth: document.documentElement.clientWidth, 32 | deviceHeight: document.documentElement.clientHeight 33 | }; 34 | 35 | // 根据模式判断页面缩放比例 36 | this.mode = this.props.mode || "auto"; 37 | 38 | // 缩放中心 39 | this.origin = this.props.origin || "left top 0"; 40 | 41 | // 传入的视觉稿 42 | this.visualWidth = this.props.width || 320; 43 | 44 | this.visualHeight = this.props.height || 504; 45 | 46 | this.wrapperBackgroundColor = this.props.wrapperBackgroundColor || "black"; 47 | 48 | this._calcScaleRatio = this._calcScaleRatio.bind(this); 49 | 50 | this._updateDimensions = this._updateDimensions.bind(this); 51 | } 52 | 53 | /** 54 | * @function 为了避免重绘,在ComponentWillMount之前 55 | */ 56 | 57 | componentDidMount() { 58 | // 监听页面尺寸变化 59 | window.addEventListener("resize", this._updateDimensions); 60 | } 61 | 62 | componentWillUnmount() { 63 | // 移除页面尺寸变化监听 64 | window.removeEventListener("resize", this._updateDimensions); 65 | } 66 | 67 | /** 68 | * @function 更新屏幕尺寸 69 | * @private 70 | */ 71 | _updateDimensions() { 72 | this.setState({ 73 | deviceWidth: document.documentElement.clientWidth, 74 | deviceHeight: document.documentElement.clientHeight 75 | }); 76 | } 77 | 78 | /** 79 | * @function 计算缩放参数 80 | * @private 81 | */ 82 | _calcScaleRatio() { 83 | // 默认缩放比例为1 84 | let scaleRatio = 1; 85 | 86 | const deviceAspectRatio = this.state.deviceWidth / this.state.deviceHeight; 87 | 88 | // 计算传入的视觉稿的比 89 | const visualAspectRatio = this.visualWidth / this.visualHeight; 90 | 91 | // 计算缩放比 92 | if (this.mode === "contain") { 93 | // 如果是包含模式,根据宽高中较大值进行缩放 94 | scaleRatio = deviceAspectRatio > visualAspectRatio 95 | ? this.state.deviceHeight / this.visualHeight 96 | : this.state.deviceWidth / this.visualWidth; 97 | } else if (this.mode === "cover") { 98 | scaleRatio = deviceAspectRatio < visualAspectRatio 99 | ? this.state.deviceHeight / this.visualHeight 100 | : this.state.deviceWidth / this.visualWidth; 101 | } else { 102 | scaleRatio = this.state.deviceWidth / this.visualWidth; 103 | } 104 | 105 | return scaleRatio; 106 | } 107 | 108 | /** 109 | * @function 默认的渲染函数 110 | * @returns {XML} 111 | */ 112 | render() { 113 | const scaleRatio = this._calcScaleRatio(); 114 | 115 | // 设置包裹层样式 116 | const wrapperStyle = { 117 | position: "relative", 118 | width: "100%", 119 | height: "100%", 120 | backgroundColor: this.wrapperBackgroundColor, 121 | overflow: "hidden" 122 | }; 123 | 124 | // 设置内部元素的缩放属性 125 | const scaleStyle = { 126 | width: this.visualWidth, 127 | height: this.visualHeight, 128 | WebkitTransformOrigin: this.origin, 129 | transformOrigin: this.origin, 130 | WebkitTransform: `scale(${scaleRatio})`, 131 | transform: `scale(${scaleRatio})` 132 | }; 133 | 134 | // 兼容android 2.3.5系统下body高度不自动刷新的bug 135 | if (this.mode === "auto" && this.android) { 136 | document.body.style.height = `${this.visualHeight * scaleRatio }px`; 137 | } else if (this.mode === "contain" || this.mode === "cover") { 138 | // 如果是contain模式 139 | // 设置为绝对定位 140 | scaleStyle.position = "absolute"; 141 | 142 | scaleStyle.left = `${(this.state.deviceWidth - this.visualWidth) / 2 }px`; 143 | 144 | scaleStyle.top = `${(this.state.deviceHeight - this.visualHeight) / 2 }px`; 145 | 146 | scaleStyle.WebkitTransformOrigin = "center center 0"; 147 | 148 | scaleStyle.transformOrigin = "center center 0"; 149 | 150 | // 阻止默认滑屏事件 151 | if (this.wp) { 152 | document.body.style.msTouchAction = "none"; 153 | } else { 154 | document.ontouchmove = function(e) { 155 | e.preventDefault(); 156 | }; 157 | } 158 | } 159 | 160 | return ( 161 |
162 |
163 | {/* 直接将子元素放置在这里 */} 164 | {this.props.children} 165 |
166 |
167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /interactable.ts/src/scroll/pull-to-refresh/wptr.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import Hammer from 'hammerjs'; 4 | 5 | export default function WebPullToRefresh() { 6 | 7 | 8 | /** 9 | * Hold all of the default parameters for the module 10 | * @type {object} 11 | */ 12 | const defaults = { 13 | // ID of the element holding pannable content area 14 | contentEl: 'content', 15 | 16 | // ID of the element holding pull to refresh loading area 17 | ptrEl: 'ptr', 18 | 19 | // wrapper element holding scollable 20 | bodyEl: document.body, 21 | 22 | // Number of pixels of panning until refresh 23 | distanceToRefresh: 70, 24 | 25 | // Pointer to function that does the loading and returns a promise 26 | loadingFunction: false, 27 | 28 | // Dragging resistance level 29 | resistance: 2.5 30 | }; 31 | 32 | /** 33 | * Hold all of the merged parameter and default module options 34 | * @type {object} 35 | */ 36 | let options = {}; 37 | 38 | /** 39 | * Pan event parameters 40 | * @type {object} 41 | */ 42 | const pan = { 43 | enabled: false, 44 | distance: 0, 45 | startingPositionY: 0 46 | }; 47 | 48 | /** 49 | * Easy shortener for handling adding and removing body classes. 50 | */ 51 | let bodyClass = defaults.bodyEl.classList; 52 | 53 | /** 54 | * Initialize pull to refresh, hammer, and bind pan events. 55 | * 56 | * @param {object=} params - Setup parameters for pull to refresh 57 | */ 58 | const init = function(params) { 59 | params = params || {}; 60 | options = { 61 | contentEl: 62 | params.contentEl || document.getElementById(defaults.contentEl), 63 | ptrEl: params.ptrEl || document.getElementById(defaults.ptrEl), 64 | bodyEl: params.bodyEl || defaults.bodyEl, 65 | distanceToRefresh: params.distanceToRefresh || defaults.distanceToRefresh, 66 | loadingFunction: params.loadingFunction || defaults.loadingFunction, 67 | resistance: params.resistance || defaults.resistance, 68 | hammerOptions: params.hammerOptions || {} 69 | }; 70 | 71 | if (!options.contentEl || !options.ptrEl) { 72 | return false; 73 | } 74 | 75 | bodyClass = options.bodyEl.classList; 76 | 77 | const h = new Hammer(options.contentEl, options.hammerOptions); 78 | 79 | h.get('pan').set({ direction: Hammer.DIRECTION_VERTICAL }); 80 | 81 | h.on('panstart', _panStart); 82 | h.on('pandown', _panDown); 83 | h.on('panup', _panUp); 84 | h.on('panend', _panEnd); 85 | }; 86 | 87 | /** 88 | * Determine whether pan events should apply based on scroll position on panstart 89 | * 90 | * @param {object} e - Event object 91 | */ 92 | var _panStart = function(e) { 93 | pan.startingPositionY = options.bodyEl.getAttribute('inner-scroll-top') 94 | ? options.bodyEl.getAttribute('inner-scroll-top') 95 | : options.bodyEl.scrollTop; 96 | 97 | // 判断是否允许下拉刷新 98 | if (pan.startingPositionY < 10) { 99 | pan.enabled = true; 100 | } else { 101 | pan.enabled = false; 102 | } 103 | }; 104 | 105 | /** 106 | * Handle element on screen movement when the pandown events is firing. 107 | * 108 | * @param {object} e - Event object 109 | */ 110 | var _panDown = function(e) { 111 | if (!pan.enabled) { 112 | return; 113 | } 114 | 115 | e.preventDefault(); 116 | pan.distance = e.distance / options.resistance; 117 | 118 | _setContentPan(); 119 | _setBodyClass(); 120 | }; 121 | 122 | /** 123 | * Handle element on screen movement when the pandown events is firing. 124 | * 125 | * @param {object} e - Event object 126 | */ 127 | var _panUp = function(e) { 128 | if (!pan.enabled || pan.distance === 0) { 129 | return; 130 | } 131 | 132 | e.preventDefault(); 133 | 134 | if (pan.distance < e.distance / options.resistance) { 135 | pan.distance = 0; 136 | } else { 137 | pan.distance = e.distance / options.resistance; 138 | } 139 | 140 | _setContentPan(); 141 | _setBodyClass(); 142 | }; 143 | 144 | /** 145 | * Set the CSS transform on the content element to move it on the screen. 146 | */ 147 | var _setContentPan = function() { 148 | // Use transforms to smoothly animate elements on desktop and mobile devices 149 | options.contentEl.style.transform = options.contentEl.style.webkitTransform = 150 | `translate3d( 0, ${ pan.distance }px, 0 )`; 151 | options.ptrEl.style.transform = options.ptrEl.style.webkitTransform = 152 | `translate3d( 0, ${ 153 | pan.distance - options.ptrEl.offsetHeight 154 | }px, 0 )`; 155 | 156 | // 设置最多两秒之后回复 157 | setTimeout(() => { 158 | options.contentEl.style.transform = options.contentEl.style.webkitTransform = 159 | ''; 160 | options.ptrEl.style.transform = options.ptrEl.style.webkitTransform = ''; 161 | }, 1500); 162 | }; 163 | 164 | /** 165 | * Set/remove the loading body class to show or hide the loading indicator after pull down. 166 | */ 167 | var _setBodyClass = function() { 168 | if (pan.distance > options.distanceToRefresh) { 169 | bodyClass.add('ptr-refresh'); 170 | } else { 171 | bodyClass.remove('ptr-refresh'); 172 | } 173 | }; 174 | 175 | /** 176 | * Determine how to animate and position elements when the panend event fires. 177 | * 178 | * @param {object} e - Event object 179 | */ 180 | var _panEnd = function(e) { 181 | if (!pan.enabled) { 182 | return; 183 | } 184 | 185 | e.preventDefault(); 186 | 187 | options.contentEl.style.transform = options.contentEl.style.webkitTransform = 188 | ''; 189 | options.ptrEl.style.transform = options.ptrEl.style.webkitTransform = ''; 190 | 191 | if (options.bodyEl.classList.contains('ptr-refresh')) { 192 | _doLoading(); 193 | } else { 194 | _doReset(); 195 | } 196 | 197 | pan.distance = 0; 198 | pan.enabled = false; 199 | }; 200 | 201 | /** 202 | * Position content and refresh elements to show that loading is taking place. 203 | */ 204 | var _doLoading = function() { 205 | bodyClass.add('ptr-loading'); 206 | 207 | // If no valid loading function exists, just reset elements 208 | if (!options.loadingFunction) { 209 | return _doReset(); 210 | } 211 | 212 | // The loading function should return a promise 213 | const loadingPromise = options.loadingFunction(); 214 | 215 | // For UX continuity, make sure we show loading for at least one second before resetting 216 | setTimeout(() => { 217 | // Once actual loading is complete, reset pull to refresh 218 | loadingPromise.then(_doReset); 219 | }, 1000); 220 | }; 221 | 222 | /** 223 | * Reset all elements to their starting positions before any paning took place. 224 | */ 225 | var _doReset = function() { 226 | bodyClass.remove('ptr-loading'); 227 | bodyClass.remove('ptr-refresh'); 228 | bodyClass.add('ptr-reset'); 229 | 230 | var bodyClassRemove = function() { 231 | bodyClass.remove('ptr-reset'); 232 | options.bodyEl.removeEventListener( 233 | 'transitionend', 234 | bodyClassRemove, 235 | false 236 | ); 237 | }; 238 | 239 | options.bodyEl.addEventListener('transitionend', bodyClassRemove, false); 240 | }; 241 | 242 | return { 243 | init 244 | }; 245 | } 246 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/components/Carousel/index.less: -------------------------------------------------------------------------------- 1 | @fc-screen-sm-min: 768px; 2 | @fc-screen-xsm-min: 480px; 3 | @fc-white: #fff; 4 | @fc-black: #000; 5 | @fc-blue: #337ab7; 6 | @fc-background-black: rgba(0, 0, 0, 0.4); 7 | @fc-transparent: rgba(0, 0, 0, 0); 8 | 9 | .fc-gallery-carousel { 10 | -webkit-tap-highlight-color: @fc-transparent; 11 | 12 | &.fullscreen-modal { 13 | background: @fc-black; 14 | bottom: 0; 15 | height: 100%; 16 | left: 0; 17 | position: fixed; 18 | right: 0; 19 | top: 0; 20 | width: 100%; 21 | z-index: 5; 22 | 23 | .fc-gallery-carousel-content { 24 | top: 50%; 25 | transform: translateY(-50%); 26 | } 27 | } 28 | } 29 | 30 | .fc-gallery-carousel-content { 31 | height: 100%; 32 | position: relative; 33 | line-height: 0; 34 | top: 0; 35 | 36 | &.fullscreen { 37 | background: @fc-black; 38 | 39 | .fc-gallery-carousel-slide { 40 | background: @fc-black; 41 | } 42 | } 43 | } 44 | 45 | .fc-gallery-carousel-slide-wrapper { 46 | position: relative; 47 | 48 | &.left, 49 | &.right { 50 | display: inline-block; 51 | width: calc(100% - 113px); // 100px + 8px for border + 5px for padding 52 | 53 | @media (max-width: @fc-screen-sm-min) { 54 | width: calc(100% - 84px); // 75px + 6px for border + 3px for padding 55 | } 56 | } 57 | &.fc-gallery-carousel-rtl { 58 | direction: rtl; 59 | } 60 | } 61 | 62 | .fc-gallery-carousel-fullscreen-button, 63 | .fc-gallery-carousel-play-button, 64 | .fc-gallery-carousel-left-nav, 65 | .fc-gallery-carousel-right-nav { 66 | appearance: none; 67 | background-color: transparent; 68 | border: 0; 69 | cursor: pointer; 70 | outline: none; 71 | position: absolute; 72 | z-index: 4; 73 | } 74 | 75 | .fc-gallery-carousel-fullscreen-button, 76 | .fc-gallery-carousel-play-button { 77 | bottom: 0; 78 | } 79 | 80 | .fc-gallery-carousel-fullscreen-button { 81 | right: 0; 82 | } 83 | 84 | .fc-gallery-carousel-play-button { 85 | left: 0; 86 | } 87 | 88 | .fc-gallery-carousel-left-nav, 89 | .fc-gallery-carousel-right-nav { 90 | color: @fc-white; 91 | font-size: 5em; 92 | padding: 50px 15px; 93 | top: 50%; 94 | transform: translateY(-50%); 95 | 96 | &[disabled] { 97 | cursor: disabled; 98 | opacity: 0.6; 99 | pointer-events: none; 100 | } 101 | 102 | @media (max-width: @fc-screen-sm-min) { 103 | font-size: 3.4em; 104 | padding: 20px 15px; 105 | } 106 | 107 | @media (max-width: @fc-screen-xsm-min) { 108 | font-size: 2.4em; 109 | padding: 0 15px; 110 | } 111 | } 112 | 113 | .fc-gallery-carousel-left-nav { 114 | left: 0; 115 | } 116 | 117 | .fc-gallery-carousel-right-nav { 118 | right: 0; 119 | } 120 | 121 | .fc-gallery-carousel-slides { 122 | line-height: 0; 123 | overflow: hidden; 124 | position: relative; 125 | white-space: nowrap; 126 | } 127 | 128 | .fc-gallery-carousel-slide { 129 | background: @fc-white; 130 | left: 0; 131 | position: absolute; 132 | top: 0; 133 | width: 100%; 134 | 135 | &.center { 136 | position: relative; 137 | } 138 | 139 | img { 140 | width: 100%; 141 | } 142 | 143 | .fc-gallery-carousel-description { 144 | background: @fc-background-black; 145 | bottom: 70px; 146 | color: @fc-white; 147 | left: 0; 148 | line-height: 1; 149 | padding: 10px 20px; 150 | position: absolute; 151 | white-space: normal; 152 | 153 | @media (max-width: @fc-screen-sm-min) { 154 | bottom: 45px; 155 | font-size: 0.8em; 156 | padding: 8px 15px; 157 | } 158 | } 159 | } 160 | 161 | .fc-gallery-carousel-bullets { 162 | bottom: 20px; 163 | left: 0; 164 | margin: 0 auto; 165 | position: absolute; 166 | right: 0; 167 | width: 80%; 168 | z-index: 4; 169 | 170 | .fc-gallery-carousel-bullets-container { 171 | margin: 0; 172 | padding: 0; 173 | text-align: center; 174 | } 175 | 176 | .fc-gallery-carousel-bullet { 177 | appearance: none; 178 | background-color: transparent; 179 | border: 1px solid @fc-white; 180 | border-radius: 50%; 181 | box-shadow: 0 1px 0 lighten(@fc-black, 10%); 182 | cursor: pointer; 183 | display: inline-block; 184 | margin: 0 5px; 185 | outline: none; 186 | padding: 5px; 187 | 188 | @media (max-width: @fc-screen-sm-min) { 189 | margin: 0 3px; 190 | padding: 3px; 191 | } 192 | 193 | @media (max-width: @fc-screen-xsm-min) { 194 | padding: 2.7px; 195 | } 196 | 197 | &.active { 198 | background: @fc-white; 199 | } 200 | } 201 | } 202 | 203 | .fc-gallery-carousel-thumbnails-wrapper { 204 | position: relative; 205 | height: 100%; 206 | 207 | &.thumbnails-wrapper-rtl { 208 | direction: rtl; 209 | } 210 | &.left, 211 | &.right { 212 | display: inline-block; 213 | vertical-align: top; 214 | width: 108px; // 100px + 8px for border 215 | 216 | @media (max-width: @fc-screen-sm-min) { 217 | width: 81px; // 75px + 6px for border 218 | } 219 | 220 | .fc-gallery-carousel-thumbnails { 221 | height: 100%; 222 | width: 100%; 223 | left: 0; 224 | padding: 0; 225 | position: absolute; 226 | top: 0; 227 | box-sizing: border-box; 228 | 229 | .fc-gallery-carousel-thumbnail { 230 | display: block; 231 | margin-right: 0; 232 | padding: 0; 233 | 234 | + .fc-gallery-carousel-thumbnail { 235 | margin-left: 0; 236 | } 237 | } 238 | } 239 | } 240 | 241 | &.left { 242 | margin-right: 5px; 243 | 244 | @media (max-width: @fc-screen-sm-min) { 245 | margin-right: 3px; 246 | } 247 | } 248 | 249 | &.right { 250 | margin-left: 5px; 251 | 252 | @media (max-width: @fc-screen-sm-min) { 253 | margin-left: 3px; 254 | } 255 | } 256 | } 257 | 258 | .fc-gallery-carousel-thumbnails { 259 | overflow: hidden; 260 | 261 | @media (max-width: @fc-screen-sm-min) { 262 | padding: 3px 0; 263 | } 264 | 265 | &-container { 266 | cursor: pointer; 267 | text-align: center; 268 | transition: transform 0.45s ease-out; 269 | white-space: nowrap; 270 | height: 100%; 271 | display: flex; 272 | } 273 | } 274 | 275 | .fc-gallery-carousel-thumbnail { 276 | display: inline-block; 277 | border: 4px solid transparent; 278 | transition: border 0.3s ease-out; 279 | width: 200px; 280 | box-sizing: border-box; 281 | 282 | &-icons { 283 | // display: none; 284 | position: absolute; 285 | right: 5px; 286 | top: 5px; 287 | } 288 | 289 | &-inner:hover { 290 | .fc-gallery-carousel-thumbnail-icons { 291 | display: block; 292 | } 293 | } 294 | 295 | @media (max-width: @fc-screen-sm-min) { 296 | border: 3px solid transparent; 297 | width: 75px; 298 | } 299 | 300 | + .fc-gallery-carousel-thumbnail { 301 | margin-left: 2px; 302 | } 303 | 304 | .fc-gallery-carousel-thumbnail-inner { 305 | position: relative; 306 | height: 100%; 307 | width: 100%; 308 | display: flex; 309 | align-items: center; 310 | justify-content: center; 311 | } 312 | 313 | img { 314 | vertical-align: middle; 315 | max-width: 100%; 316 | max-height: 100%; 317 | } 318 | 319 | &.active { 320 | border: 4px solid @fc-blue; 321 | 322 | @media (max-width: @fc-screen-sm-min) { 323 | border: 3px solid @fc-blue; 324 | } 325 | } 326 | } 327 | 328 | .fc-gallery-carousel-thumbnail-label { 329 | box-sizing: border-box; 330 | color: white; 331 | font-size: 1em; 332 | left: 0; 333 | line-height: 1em; 334 | padding: 5%; 335 | position: absolute; 336 | top: 50%; 337 | text-shadow: 1px 1px 0 black; 338 | transform: translateY(-50%); 339 | white-space: normal; 340 | width: 100%; 341 | 342 | @media (max-width: @fc-screen-sm-min) { 343 | font-size: 0.8em; 344 | line-height: 0.8em; 345 | } 346 | } 347 | 348 | .fc-gallery-carousel-index { 349 | background: @fc-background-black; 350 | color: @fc-white; 351 | line-height: 1; 352 | padding: 10px 20px; 353 | position: absolute; 354 | right: 0; 355 | top: 0; 356 | z-index: 4; 357 | 358 | @media (max-width: @fc-screen-sm-min) { 359 | font-size: 0.8em; 360 | padding: 5px 10px; 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /gallery.ts/scripts/template/template.ejs: -------------------------------------------------------------------------------- 1 | <% var item, key %> 2 | <% htmlWebpackPlugin.options.appMountIds = htmlWebpackPlugin.options.appMountIds || [] %> 3 | <% htmlWebpackPlugin.options.lang = htmlWebpackPlugin.options.lang || "en" %> 4 | <% htmlWebpackPlugin.options.links = htmlWebpackPlugin.options.links || [] %> 5 | <% htmlWebpackPlugin.options.meta = htmlWebpackPlugin.options.meta || [] %> 6 | <% htmlWebpackPlugin.options.scripts = htmlWebpackPlugin.options.scripts || []%> 7 | 8 | 9 | manifest="<%= htmlWebpackPlugin.files.manifest %>" <% } %>> 10 | 11 | 12 | 13 | 14 | 15 | <%if (htmlWebpackPlugin.options.useCDN) { %> 16 | 17 | 18 | 19 | 20 | 21 | <% } %> 22 | 23 | <%if (htmlWebpackPlugin.options.baseHref) { %> 24 | 25 | <% } %> 26 | 27 | <% if (Array.isArray(htmlWebpackPlugin.options.meta)) { %> 28 | <% for (item of htmlWebpackPlugin.options.meta) { %> 29 | <%= key %>="<%= item[key] %>" <% } %>><% 30 | } %> 31 | <% } %> 32 | <% 33 | %> 34 | <%= htmlWebpackPlugin.options.title %><% 35 | 36 | if (htmlWebpackPlugin.files.favicon) { %> 37 | <% 38 | } %><% 39 | 40 | if (htmlWebpackPlugin.options.mobile) { %> 41 | <% 42 | } %><% 43 | 44 | for (item of htmlWebpackPlugin.options.links) { %><% 45 | if (typeof item === 'string' || item instanceof String) { item = { href: item, rel: 'stylesheet' } } %> 46 | <%= key %>="<%= item[key] %>" <% } %> /><% 47 | } %><% 48 | 49 | for (key in htmlWebpackPlugin.files.css) { %><% 50 | if (htmlWebpackPlugin.files.cssIntegrity) { %> 51 | <% 54 | } else { %> 55 | <% 56 | } %><% 57 | } %><% 58 | if (htmlWebpackPlugin.options.headHtmlSnippet) { %> 59 | <%= htmlWebpackPlugin.options.headHtmlSnippet %><% 60 | } %> 61 | 62 | 72 | 154 | 155 | 156 | <% 157 | if (htmlWebpackPlugin.options.unsupportedBrowser) { %> 158 | 163 |
164 | Sorry, your browser is not supported. Please upgrade to the latest version or switch your browser to use this 165 | site. See outdatedbrowser.com for options. 166 |
<% 167 | } %><% 168 | 169 | if (htmlWebpackPlugin.options.bodyHtmlSnippet) { %> 170 | <%= htmlWebpackPlugin.options.bodyHtmlSnippet %><% 171 | } %><% 172 | 173 | if (htmlWebpackPlugin.options.appMountId) { %> 174 |
<% 175 | if (htmlWebpackPlugin.options.appMountHtmlSnippet) { %> 176 | <%= htmlWebpackPlugin.options.appMountHtmlSnippet %><% 177 | } %> 178 |
<% 179 | } %><% 180 | 181 | for (item of htmlWebpackPlugin.options.appMountIds) { %> 182 |
<% 183 | } %><% 184 | 185 | if (htmlWebpackPlugin.options.window) { %> 186 | <% 191 | } %><% 192 | 193 | if (htmlWebpackPlugin.options.inlineManifestWebpackName) { %> 194 | <%= htmlWebpackPlugin.files[htmlWebpackPlugin.options.inlineManifestWebpackName] %><% 195 | } %><% 196 | 197 | for (item of htmlWebpackPlugin.options.scripts) { %><% 198 | if (typeof item === 'string' || item instanceof String) { item = { src: item, type: 'text/javascript' } } %> 199 | <%= key %>= "<%= item[key] %>" <% } %>> 200 | <% 201 | } %><% 202 | 203 | for (key in htmlWebpackPlugin.files.chunks) { %><% 204 | if (htmlWebpackPlugin.files.jsIntegrity) { %> 205 | <% 208 | } else { %> 209 | <% 210 | } %><% 211 | } %> 212 | 213 | <% if (htmlWebpackPlugin.options.devServer) { %> 214 | 215 | <% } %> 216 | 217 |
218 |
219 |
220 | 221 | 256 | 257 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/example/pages/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Carousel } from '../../src'; 3 | 4 | import './App.less'; 5 | 6 | const PREFIX_URL = 'https://raw.githubusercontent.com/xiaolin/react-image-gallery/master/static/'; 7 | 8 | export class App extends React.Component { 9 | images: any[]; 10 | _imageGallery: any; 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | showIndex: false, 17 | showBullets: true, 18 | infinite: true, 19 | showThumbnails: true, 20 | showFullscreenButton: true, 21 | showGalleryFullscreenButton: true, 22 | showPlayButton: true, 23 | showGalleryPlayButton: true, 24 | showNav: true, 25 | isRTL: false, 26 | slideDuration: 450, 27 | slideInterval: 2000, 28 | slideOnThumbnailOver: false, 29 | thumbnailPosition: 'bottom', 30 | showVideo: {} 31 | }; 32 | 33 | this.images = [ 34 | { 35 | thumbnail: `${PREFIX_URL}4v.jpg`, 36 | original: `${PREFIX_URL}4v.jpg`, 37 | embedUrl: 'https://www.youtube.com/embed/4pSzhZ76GdM?autoplay=1&showinfo=0', 38 | description: 'Render custom slides within the gallery', 39 | renderItem: this._renderVideo.bind(this) 40 | }, 41 | { 42 | original: `${PREFIX_URL}image_set_default.jpg`, 43 | thumbnail: `${PREFIX_URL}image_set_thumb.jpg`, 44 | imageSet: [ 45 | { 46 | srcSet: `${PREFIX_URL}image_set_cropped.jpg`, 47 | media: '(max-width: 1280px)' 48 | }, 49 | { 50 | srcSet: `${PREFIX_URL}image_set_default.jpg`, 51 | media: '(min-width: 1280px)' 52 | } 53 | ] 54 | }, 55 | { 56 | original: `${PREFIX_URL}1.jpg`, 57 | thumbnail: `${PREFIX_URL}1t.jpg`, 58 | originalClass: 'featured-slide', 59 | thumbnailClass: 'featured-thumb', 60 | description: 'Custom class for slides & thumbnails' 61 | }, 62 | { 63 | original: 64 | 'https://unionfab-dev.oss-cn-hangzhou.aliyuncs.com/1/201911/9538f330-4c51-46b9-9ed6-43a9eab64af6', 65 | thumbnail: 66 | 'https://unionfab-dev.oss-cn-hangzhou.aliyuncs.com/1/201911/9538f330-4c51-46b9-9ed6-43a9eab64af6' 67 | }, 68 | { 69 | origin: 70 | 'https://unionfab-dev.oss-cn-hangzhou.aliyuncs.com/1/201911/db486340-f820-426b-a1b4-084f7c63dcce', 71 | thumbnail: 72 | 'https://unionfab-dev.oss-cn-hangzhou.aliyuncs.com/1/201911/db486340-f820-426b-a1b4-084f7c63dcce' 73 | } 74 | ].concat(this._getStaticImages()); 75 | } 76 | 77 | componentDidUpdate(prevProps, prevState) { 78 | if ( 79 | this.state.slideInterval !== prevState.slideInterval || 80 | this.state.slideDuration !== prevState.slideDuration 81 | ) { 82 | // refresh setInterval 83 | this._imageGallery.pause(); 84 | this._imageGallery.play(); 85 | } 86 | } 87 | 88 | _onImageClick(event) { 89 | console.debug( 90 | 'clicked on image', 91 | event.target, 92 | 'at index', 93 | this._imageGallery.getCurrentIndex() 94 | ); 95 | } 96 | 97 | _onImageLoad(event) { 98 | console.debug('loaded image', event.target.src); 99 | } 100 | 101 | _onSlide(index) { 102 | this._resetVideo(); 103 | console.debug('slid to index', index); 104 | } 105 | 106 | _onPause(index) { 107 | console.debug('paused on index', index); 108 | } 109 | 110 | _onScreenChange(fullScreenElement) { 111 | console.debug('isFullScreen?', !!fullScreenElement); 112 | } 113 | 114 | _onPlay(index) { 115 | console.debug('playing from index', index); 116 | } 117 | 118 | _handleInputChange(state, event) { 119 | this.setState({ [state]: event.target.value }); 120 | } 121 | 122 | _handleCheckboxChange(state, event) { 123 | this.setState({ [state]: event.target.checked }); 124 | } 125 | 126 | _handleThumbnailPositionChange(event) { 127 | this.setState({ thumbnailPosition: event.target.value }); 128 | } 129 | 130 | _getStaticImages() { 131 | const images: any[] = []; 132 | for (let i = 2; i < 12; i++) { 133 | images.push({ 134 | original: `${PREFIX_URL}${i}.jpg`, 135 | thumbnail: `${PREFIX_URL}${i}t.jpg` 136 | }); 137 | } 138 | 139 | return images; 140 | } 141 | 142 | _resetVideo() { 143 | this.setState({ showVideo: {} }); 144 | 145 | if (this.state.showPlayButton) { 146 | this.setState({ showGalleryPlayButton: true }); 147 | } 148 | 149 | if (this.state.showFullscreenButton) { 150 | this.setState({ showGalleryFullscreenButton: true }); 151 | } 152 | } 153 | 154 | _toggleShowVideo(url) { 155 | this.state.showVideo[url] = !Boolean(this.state.showVideo[url]); 156 | this.setState({ 157 | showVideo: this.state.showVideo 158 | }); 159 | 160 | if (this.state.showVideo[url]) { 161 | if (this.state.showPlayButton) { 162 | this.setState({ showGalleryPlayButton: false }); 163 | } 164 | 165 | if (this.state.showFullscreenButton) { 166 | this.setState({ showGalleryFullscreenButton: false }); 167 | } 168 | } 169 | } 170 | 171 | _renderVideo(item) { 172 | return ( 173 |
174 | {this.state.showVideo[item.embedUrl] ? ( 175 |
176 | 180 | 187 |
188 | ) : ( 189 | 190 |
191 | 192 | {item.description && ( 193 | 194 | {item.description} 195 | 196 | )} 197 |
198 | )} 199 |
200 | ); 201 | } 202 | 203 | render() { 204 | return ( 205 |
206 | (this._imageGallery = i)} 208 | items={this.images} 209 | thumbnailWidth={200} 210 | thumbnailHeight={150} 211 | showSlider={false} 212 | lazyLoad={false} 213 | infinite={this.state.infinite} 214 | showBullets={this.state.showBullets} 215 | showFullscreenButton={ 216 | this.state.showFullscreenButton && this.state.showGalleryFullscreenButton 217 | } 218 | showPlayButton={this.state.showPlayButton && this.state.showGalleryPlayButton} 219 | showThumbnails={this.state.showThumbnails} 220 | showIndex={this.state.showIndex} 221 | showNav={this.state.showNav} 222 | isRTL={this.state.isRTL} 223 | thumbnailPosition={this.state.thumbnailPosition} 224 | slideDuration={parseInt(this.state.slideDuration)} 225 | slideInterval={parseInt(this.state.slideInterval)} 226 | slideOnThumbnailOver={this.state.slideOnThumbnailOver} 227 | additionalClass="app-image-gallery" 228 | onClick={this._onImageClick.bind(this)} 229 | onImageLoad={this._onImageLoad} 230 | onSlide={this._onSlide.bind(this)} 231 | onPause={this._onPause.bind(this)} 232 | onScreenChange={this._onScreenChange.bind(this)} 233 | onPlay={this._onPlay.bind(this)} 234 | onThumbnailDelete={(...args) => { 235 | console.log(args); 236 | }} 237 | /> 238 | 239 |
240 |
241 |

Settings

242 | 243 |
    244 |
  • 245 |
    246 | Play Interval 247 | 253 |
    254 |
  • 255 | 256 |
  • 257 |
    258 | Slide Duration 259 | 265 |
    266 |
  • 267 | 268 |
  • 269 |
    270 | Thumbnail Bar Position 271 | 281 |
    282 |
  • 283 |
284 | 285 |
    286 |
  • 287 | 293 | 294 |
  • 295 |
  • 296 | 302 | 303 |
  • 304 |
  • 305 | 311 | 312 |
  • 313 |
  • 314 | 320 | 321 |
  • 322 |
  • 323 | 329 | 330 |
  • 331 |
  • 332 | 338 | 339 |
  • 340 |
  • 341 | 347 | 348 |
  • 349 |
  • 350 | 356 | 357 |
  • 358 |
  • 359 | 365 | 366 |
  • 367 |
368 |
369 |
370 |
371 | ); 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /gallery.ts/packages/fc-gallery-react/src/components/Carousel/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Swipeable } from 'react-swipeable'; 3 | import throttle from 'lodash.throttle'; 4 | import debounce from 'lodash.debounce'; 5 | import ResizeObserver from 'resize-observer-polyfill'; 6 | import Lightbox from 'react-image-lightbox'; 7 | import 'react-image-lightbox/style.css'; 8 | 9 | import './index.less'; 10 | import X from '../../assets/X.svg'; 11 | import { Image } from '../../types'; 12 | 13 | const screenChangeEvents = [ 14 | 'fullscreenchange', 15 | 'MSFullscreenChange', 16 | 'mozfullscreenchange', 17 | 'webkitfullscreenchange' 18 | ]; 19 | 20 | export interface ICarouselProps { 21 | flickThreshold: number; 22 | items: Image[]; 23 | 24 | thumbnailWidth: number; 25 | thumbnailHeight: number; 26 | thumbnailPosition: string; 27 | disableThumbnailScroll: boolean; 28 | slideOnThumbnailOver: boolean; 29 | thumbnailWithLightbox: boolean; 30 | 31 | showNav: boolean; 32 | autoPlay: boolean; 33 | lazyLoad: boolean; 34 | infinite: boolean; 35 | showIndex: boolean; 36 | showBullets: boolean; 37 | showSlider: boolean; 38 | showThumbnails: boolean; 39 | showPlayButton: boolean; 40 | showFullscreenButton: boolean; 41 | disableArrowKeys: boolean; 42 | disableSwipe: boolean; 43 | useBrowserFullscreen: boolean; 44 | preventDefaultTouchmoveEvent: boolean; 45 | defaultImage: string; 46 | indexSeparator: string; 47 | startIndex: number; 48 | slideDuration: number; 49 | slideInterval: number; 50 | swipeThreshold: number; 51 | swipingTransitionDuration: number; 52 | 53 | onSlide: any; 54 | onScreenChange: any; 55 | onPause: any; 56 | onPlay: any; 57 | onClick: any; 58 | onImageLoad: any; 59 | onImageError: any; 60 | onTouchMove: any; 61 | onTouchEnd: any; 62 | onTouchStart: any; 63 | onMouseOver: any; 64 | onMouseLeave: any; 65 | onThumbnailError: any; 66 | onThumbnailClick: any; 67 | onThumbnailDelete: any; 68 | onBulletClick: any; 69 | 70 | renderCustomControls: any; 71 | renderLeftNav: any; 72 | renderRightNav: any; 73 | renderPlayPauseButton: any; 74 | renderFullscreenButton: any; 75 | renderItem: any; 76 | renderThumbInner: any; 77 | 78 | stopPropagation: boolean; 79 | additionalClass: string; 80 | useTranslate3D: boolean; 81 | isRTL: boolean; 82 | } 83 | 84 | export interface ICarouselState { 85 | currentIndex: number; 86 | offsetPercentage: number; 87 | galleryWidth: number; 88 | thumbsTranslate: number; 89 | // 水平状态下缩略图的总外部容器的宽度 90 | thumbnailsWrapperWidth: number; 91 | // 垂直状态下缩略图的总外部容器的高度 92 | thumbnailsWrapperHeight: number; 93 | 94 | style?: any; 95 | 96 | isFullscreen: boolean; 97 | isPlaying: boolean; 98 | isTransitioning?: boolean; 99 | } 100 | 101 | export class Carousel extends React.Component, any> { 102 | static defaultProps = { 103 | items: [], 104 | 105 | showThumbnails: true, 106 | thumbnailWidth: 200, 107 | thumbnailHeight: 150, 108 | thumbnailWithLightbox: true, 109 | thumbnailPosition: 'bottom', 110 | disableThumbnailScroll: false, 111 | 112 | showNav: true, 113 | autoPlay: false, 114 | lazyLoad: false, 115 | infinite: true, 116 | showSlider: true, 117 | showIndex: false, 118 | showBullets: false, 119 | showPlayButton: true, 120 | showFullscreenButton: true, 121 | disableArrowKeys: false, 122 | disableSwipe: false, 123 | useTranslate3D: true, 124 | isRTL: false, 125 | useBrowserFullscreen: true, 126 | preventDefaultTouchmoveEvent: false, 127 | flickThreshold: 0.4, 128 | stopPropagation: false, 129 | indexSeparator: ' / ', 130 | startIndex: 0, 131 | slideDuration: 450, 132 | swipingTransitionDuration: 0, 133 | slideInterval: 3000, 134 | swipeThreshold: 30, 135 | 136 | renderLeftNav: (onClick, disabled) => { 137 | return ( 138 | 1169 | ); 1170 | } 1171 | }); 1172 | 1173 | const slideWrapper = ( 1174 |
1180 | {showSlider && ( 1181 | 1182 | {this.props.renderCustomControls && this.props.renderCustomControls()} 1183 | 1184 | {this.props.showFullscreenButton && 1185 | this.props.renderFullscreenButton(this._toggleFullScreen, isFullscreen)} 1186 | 1187 | {this.props.showPlayButton && 1188 | this.props.renderPlayPauseButton(this._togglePlay, isPlaying)} 1189 | 1190 | {this._canNavigate() ? ( 1191 | [ 1192 | this.props.showNav && ( 1193 | 1194 | {this.props.renderLeftNav(slideLeft, !this._canSlideLeft())} 1195 | {this.props.renderRightNav(slideRight, !this._canSlideRight())} 1196 | 1197 | ), 1198 | 1199 | 1206 |
{slides}
1207 |
1208 | ] 1209 | ) : ( 1210 |
{slides}
1211 | )} 1212 | 1213 | {this.props.showBullets && ( 1214 |
1215 |
1220 | {bullets} 1221 |
1222 |
1223 | )} 1224 | 1225 | {this.props.showIndex && ( 1226 |
1227 | 1228 | {this.state.currentIndex + 1} 1229 | 1230 | 1231 | {this.props.indexSeparator} 1232 | 1233 | {this.props.items!.length} 1234 |
1235 | )} 1236 |
1237 | )} 1238 |
1239 | ); 1240 | 1241 | const classNames = [ 1242 | 'fc-gallery-carousel', 1243 | this.props.additionalClass, 1244 | modalFullscreen ? 'fullscreen-modal' : '' 1245 | ] 1246 | .filter(name => typeof name === 'string') 1247 | .join(' '); 1248 | 1249 | const { items = [] } = this.props; 1250 | const { thumbnialImageIndex } = this.state; 1251 | 1252 | return ( 1253 |
(this._imageGallery = i)} className={classNames} aria-live="polite"> 1254 |
1255 | {(thumbnailPosition === 'bottom' || thumbnailPosition === 'right') && slideWrapper} 1256 | 1257 | {this.props.showThumbnails && ( 1258 |
1264 |
(this._thumbnailsWrapper = i)} 1267 | > 1268 |
(this._thumbnails = t)} 1270 | className="fc-gallery-carousel-thumbnails-container" 1271 | style={{ height: (this.props.thumbnailHeight || 0) + 8, ...thumbnailStyle }} 1272 | aria-label="Thumbnail Navigation" 1273 | > 1274 | {thumbnails} 1275 |
1276 |
1277 |
1278 | )} 1279 | 1280 | {(thumbnailPosition === 'top' || thumbnailPosition === 'left') && slideWrapper} 1281 |
1282 | 1283 | {this.state.showLightbox && this.props.thumbnailWithLightbox && ( 1284 | { 1295 | this.setState({ 1296 | showLightbox: false 1297 | }); 1298 | }} 1299 | onMovePrevRequest={() => 1300 | this.setState({ 1301 | thumbnialImageIndex: (thumbnialImageIndex + items.length - 1) % items.length 1302 | }) 1303 | } 1304 | onMoveNextRequest={() => 1305 | this.setState({ 1306 | thumbnialImageIndex: (thumbnialImageIndex + items.length + 1) % items.length 1307 | }) 1308 | } 1309 | /> 1310 | )} 1311 |
1312 | ); 1313 | } 1314 | } 1315 | --------------------------------------------------------------------------------