create-pnpm-cli 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/template/webpack-typescript-react/.env.development +1 -0
  3. package/template/webpack-typescript-react/.env.production +1 -0
  4. package/template/webpack-typescript-react/.eslintrc.js +36 -0
  5. package/template/webpack-typescript-react/babel.config.js +38 -0
  6. package/template/webpack-typescript-react/package.json +63 -0
  7. package/template/webpack-typescript-react/postcss.config.js +13 -0
  8. package/template/webpack-typescript-react/prettier.config.js +8 -0
  9. package/template/webpack-typescript-react/public/img/img1.jpg +0 -0
  10. package/template/webpack-typescript-react/public/img/img10.jpg +0 -0
  11. package/template/webpack-typescript-react/public/img/img11.jpg +0 -0
  12. package/template/webpack-typescript-react/public/img/img12.jpg +0 -0
  13. package/template/webpack-typescript-react/public/img/img13.jpg +0 -0
  14. package/template/webpack-typescript-react/public/img/img14.jpg +0 -0
  15. package/template/webpack-typescript-react/public/img/img15.jpg +0 -0
  16. package/template/webpack-typescript-react/public/img/img16.jpg +0 -0
  17. package/template/webpack-typescript-react/public/img/img17.jpg +0 -0
  18. package/template/webpack-typescript-react/public/img/img18.jpg +0 -0
  19. package/template/webpack-typescript-react/public/img/img19.jpg +0 -0
  20. package/template/webpack-typescript-react/public/img/img2.jpg +0 -0
  21. package/template/webpack-typescript-react/public/img/img20.jpg +0 -0
  22. package/template/webpack-typescript-react/public/img/img21.jpg +0 -0
  23. package/template/webpack-typescript-react/public/img/img22.jpg +0 -0
  24. package/template/webpack-typescript-react/public/img/img23.jpg +0 -0
  25. package/template/webpack-typescript-react/public/img/img24.jpg +0 -0
  26. package/template/webpack-typescript-react/public/img/img25.jpg +0 -0
  27. package/template/webpack-typescript-react/public/img/img26.jpg +0 -0
  28. package/template/webpack-typescript-react/public/img/img27.jpg +0 -0
  29. package/template/webpack-typescript-react/public/img/img28.jpg +0 -0
  30. package/template/webpack-typescript-react/public/img/img29.jpg +0 -0
  31. package/template/webpack-typescript-react/public/img/img3.jpg +0 -0
  32. package/template/webpack-typescript-react/public/img/img30.jpg +0 -0
  33. package/template/webpack-typescript-react/public/img/img31.jpg +0 -0
  34. package/template/webpack-typescript-react/public/img/img32.jpg +0 -0
  35. package/template/webpack-typescript-react/public/img/img33.jpg +0 -0
  36. package/template/webpack-typescript-react/public/img/img34.jpg +0 -0
  37. package/template/webpack-typescript-react/public/img/img35.jpg +0 -0
  38. package/template/webpack-typescript-react/public/img/img36.jpg +0 -0
  39. package/template/webpack-typescript-react/public/img/img37.jpg +0 -0
  40. package/template/webpack-typescript-react/public/img/img38.jpg +0 -0
  41. package/template/webpack-typescript-react/public/img/img4.jpg +0 -0
  42. package/template/webpack-typescript-react/public/img/img5.jpg +0 -0
  43. package/template/webpack-typescript-react/public/img/img6.jpg +0 -0
  44. package/template/webpack-typescript-react/public/img/img7.jpg +0 -0
  45. package/template/webpack-typescript-react/public/img/img8.jpg +0 -0
  46. package/template/webpack-typescript-react/public/img/img9.jpg +0 -0
  47. package/template/webpack-typescript-react/scripts/data.js +60 -0
  48. package/template/webpack-typescript-react/src/config/data.ts +197 -0
  49. package/template/webpack-typescript-react/src/config/index.ts +1 -0
  50. package/template/webpack-typescript-react/src/hooks/useImagesViewer.ts +80 -0
  51. package/template/webpack-typescript-react/src/hooks/useWindowWidth.ts +24 -0
  52. package/template/webpack-typescript-react/src/index.tsx +19 -0
  53. package/template/webpack-typescript-react/src/layouts/MainLayout.tsx +9 -0
  54. package/template/webpack-typescript-react/src/layouts/index.ts +1 -0
  55. package/template/webpack-typescript-react/src/pages/home.tsx +17 -0
  56. package/template/webpack-typescript-react/src/pages/image.tsx +24 -0
  57. package/template/webpack-typescript-react/src/pages/masonry.tsx +92 -0
  58. package/template/webpack-typescript-react/src/pages/scroll-view.tsx +36 -0
  59. package/template/webpack-typescript-react/src/pages/virtual-list.tsx +27 -0
  60. package/template/webpack-typescript-react/src/routes/config.tsx +39 -0
  61. package/template/webpack-typescript-react/src/styles/global.css +6 -0
  62. package/template/webpack-typescript-react/src/templates/index.html +12 -0
  63. package/template/webpack-typescript-react/src/types/index.d.ts +4 -0
  64. package/template/webpack-typescript-react/src/utils/index.ts +17 -0
  65. package/template/webpack-typescript-react/tailwind.config.js +8 -0
  66. package/template/webpack-typescript-react/tsconfig.json +21 -0
  67. package/template/webpack-typescript-react/webpack.config.ts +94 -0
  68. package/template/todo-react-typescript/tsconfig.json +0 -12
  69. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/README.md +0 -0
  70. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/apps/examples/package.json +0 -0
  71. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/apps/examples/tsconfig.json +0 -0
  72. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/package.json +0 -0
  73. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/packages/name/bin/index.js +0 -0
  74. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/packages/name/package.json +0 -0
  75. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript/packages/name}/tsconfig.json +0 -0
  76. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/pnpm-workspace.yaml +0 -0
  77. /package/template/{pnpm-typescript-monorepo → pnpm-monorepo-typescript}/prettier.config.cjs +0 -0
  78. /package/template/{todo-react-typescript → webpack-typescript-react}/README.md +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pnpm-cli",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "create pnpm cli",
5
5
  "type": "module",
6
6
  "main": "./lib/cli.cjs",
@@ -0,0 +1 @@
1
+ TEST='dev'
@@ -0,0 +1 @@
1
+ TEST='prod'
@@ -0,0 +1,36 @@
1
+ /** @type {import("eslint").Linter.Config} */
2
+ module.exports = {
3
+ env: {
4
+ browser: true,
5
+ es2021: true,
6
+ node: true,
7
+ },
8
+ extends: [
9
+ 'eslint:recommended',
10
+ 'plugin:@typescript-eslint/recommended',
11
+ 'plugin:react/recommended',
12
+ 'plugin:react/jsx-runtime',
13
+ 'plugin:prettier/recommended',
14
+ ],
15
+ parser: '@typescript-eslint/parser',
16
+ parserOptions: {
17
+ ecmaVersion: 'latest',
18
+ sourceType: 'module',
19
+ ecmaFeatures: {
20
+ jsx: true,
21
+ },
22
+ },
23
+ settings: {
24
+ react: {
25
+ version: 'detect',
26
+ },
27
+ },
28
+ plugins: ['@typescript-eslint', 'react'],
29
+ rules: {
30
+ '@typescript-eslint/no-var-requires': 'off',
31
+ '@typescript-eslint/no-unused-vars': 'warn',
32
+ '@typescript-eslint/no-explicit-any': 'warn',
33
+ 'react/display-name': 'off',
34
+ 'react/prop-types': 'off',
35
+ },
36
+ }
@@ -0,0 +1,38 @@
1
+ const isDevelopment = process.env.NODE_ENV !== 'production'
2
+
3
+ /** @type {import('@types/babel__core').TransformOptions} */
4
+ module.exports = {
5
+ // 执行顺序由右往左
6
+ presets: [
7
+ [
8
+ '@babel/preset-react',
9
+ {
10
+ // react18可以自动导入react
11
+ runtime: 'automatic',
12
+ // 区分开发与生产
13
+ development: isDevelopment,
14
+ },
15
+ ],
16
+ [
17
+ '@babel/preset-env',
18
+ {
19
+ // TODO 启用将某些浏览器的bug转译为ES5,babel8之后会默认开启,记得改
20
+ bugfixes: true,
21
+ useBuiltIns: 'entry',
22
+ corejs: 3,
23
+ },
24
+ ],
25
+ [
26
+ '@babel/preset-typescript',
27
+ {
28
+ // 允许命名空间
29
+ allowNamespaces: true,
30
+ // 允许声明字段
31
+ allowDeclareFields: true,
32
+ // 优化枚举为对象
33
+ optimizeConstEnums: true,
34
+ },
35
+ ],
36
+ ],
37
+ plugins: [isDevelopment && 'react-refresh/babel'].filter(Boolean),
38
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "micro-react",
3
+ "description": "",
4
+ "scripts": {
5
+ "lint": "eslint ./src/**/*.{ts,tsx}",
6
+ "dev": "cross-env NODE_ENV=development webpack serve",
7
+ "build": "cross-env NODE_ENV=production webpack",
8
+ "serve": "cross-env NODE_ENV=production webpack && npx serve ./dist"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "ahooks": "^3.7.10",
15
+ "high-order-ui": "^0.0.3",
16
+ "query-string": "^8.1.0",
17
+ "react": "^18.2.0",
18
+ "react-dom": "^18.2.0",
19
+ "react-router-dom": "^6.21.1",
20
+ "viewerjs": "^1.11.6"
21
+ },
22
+ "devDependencies": {
23
+ "prettier": "^3.2.4",
24
+ "@trivago/prettier-plugin-sort-imports": "^4.3.0",
25
+ "cross-env": "^7.0.3",
26
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
27
+ "@types/react": "^18.2.48",
28
+ "@types/react-dom": "^18.2.18",
29
+ "@types/eslint": "^8.56.2",
30
+ "@types/node": "^20.10.5",
31
+ "blurhash-webpack-plugin": "^0.0.5",
32
+ "html-webpack-plugin": "^5.6.0",
33
+ "react-refresh": "^0.14.0",
34
+ "style-loader": "^3.3.4",
35
+ "webpack-dev-server": "^4.15.1",
36
+ "webpack": "^5.89.0",
37
+ "webpack-cli": "^5.1.4",
38
+ "thread-loader": "^4.0.2",
39
+ "ts-node": "^10.9.2",
40
+ "tsconfig-paths-webpack-plugin": "^4.1.0",
41
+ "mini-css-extract-plugin": "^2.7.7",
42
+ "postcss": "^8.4.33",
43
+ "postcss-load-config": "^5.0.2",
44
+ "postcss-loader": "^8.0.0",
45
+ "postcss-preset-env": "^9.3.0",
46
+ "css-loader": "^6.9.1",
47
+ "cssnano": "^6.0.3",
48
+ "babel-loader": "^9.1.3",
49
+ "@types/babel__core": "^7.20.5",
50
+ "@babel/core": "^7.23.7",
51
+ "@babel/preset-env": "^7.23.8",
52
+ "@babel/preset-react": "^7.23.3",
53
+ "@babel/preset-typescript": "^7.23.3",
54
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
55
+ "@typescript-eslint/parser": "^6.19.0",
56
+ "eslint": "^8.56.0",
57
+ "eslint-config-prettier": "^9.1.0",
58
+ "eslint-plugin-prettier": "^5.1.3",
59
+ "eslint-plugin-react": "^7.33.2",
60
+ "tailwindcss": "^3.4.1",
61
+ "dotenv": "^16.3.2"
62
+ }
63
+ }
@@ -0,0 +1,13 @@
1
+ const isDevelopment = process.env.NODE_ENV !== 'production'
2
+
3
+ /** @type {import('postcss-load-config').Config} */
4
+ module.exports = {
5
+ plugins: [
6
+ // tailwindcss
7
+ require('tailwindcss'),
8
+ // 会自动添加polyfill和前缀,功能比autoprefix更全
9
+ require('postcss-preset-env'),
10
+ // 生产环境压缩css
11
+ !isDevelopment && require('cssnano'),
12
+ ].filter(Boolean),
13
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import("prettier").Config} */
2
+ module.exports = {
3
+ trailingComma: 'all',
4
+ tabWidth: 2,
5
+ semi: false,
6
+ singleQuote: true,
7
+ plugins: ['@trivago/prettier-plugin-sort-imports'],
8
+ }
@@ -0,0 +1,60 @@
1
+ function main() {
2
+ const data = []
3
+ let imports = ''
4
+
5
+ const arr = [
6
+ '今天在公园看到了儿童嬉戏,笑声洒满阳光。',
7
+ '漫步在街头,感受了都市的繁华与忙碌。',
8
+ '走进书店,被一本历史书籍深深吸引。',
9
+ '在咖啡馆看到了一对恋人的甜蜜。',
10
+ '去了海边,感受到了大海的浩渺与深邃。',
11
+ '在山顶看到了日出,被其壮观所震撼。',
12
+ '在夜市品尝了各种美食,感受到了人间烟火。',
13
+ '在画廊欣赏了一幅油画,被其色彩所吸引。',
14
+ '在博物馆看到了古代的文物,感受到了历史的厚重。',
15
+ '在花店看到了各种鲜花,被其美丽所吸引。',
16
+ '在街头看到了一只流浪猫,感到了生活的不易。',
17
+ '在电影院看了一部电影,被其剧情所吸引。',
18
+ '在湖边看到了天鹅的优雅,感受到了大自然的美丽。',
19
+ '在公园看到了一位老人的孤独,感到了生活的苍凉。',
20
+ '在动物园看到了各种动物,被其独特性格所吸引。',
21
+ '在超市看到了各种商品,感受到了生活的丰富。',
22
+ '在街头看到了一位街头艺人的表演,被其才华所吸引。',
23
+ '在图书馆看到了各种书籍,感受到了知识的深邃。',
24
+ '在体育场看到了一场篮球比赛,被其激烈所吸引。',
25
+ '在公园看到了春天的繁花,感受到了生命的活力。',
26
+ '在音乐会听到了美妙的旋律,被其魅力所吸引。',
27
+ '在街头看到了忙碌的人们,感受到了生活的节奏。',
28
+ '在公园看到了秋天的落叶,感受到了季节的变迁。',
29
+ '在游乐园看到了孩子们的快乐,感受到了童真的美好。',
30
+ '在山顶看到了星空,被其浩瀚所震撼。',
31
+ '在海边看到了日落,被其美丽所吸引。',
32
+ '在森林中看到了鸟儿的飞翔,感受到了自由的意义。',
33
+ '在湖边看到了鸭子的嬉戏,感受到了生活的乐趣。',
34
+ '在街头看到了夜晚的霓虹,感受到了城市的繁华。',
35
+ '在公园看到了雪景,被其纯净所吸引。',
36
+ '在图书馆看到了学生的努力,感受到了知识的力量。',
37
+ '在咖啡馆看到了一位写作者的专注,被其才华所吸引。',
38
+ '在公园看到了一位画家的创作,感受到了艺术的魅力。',
39
+ '在街头看到了一位老人的微笑,感受到了生活的温暖。',
40
+ '在电影院看到了一部经典电影,被其深度所吸引。',
41
+ '在超市看到了一位母亲的辛劳,感受到了母爱的伟大。',
42
+ '在公园看到了一位跑步的人,感受到了健康的重要。',
43
+ '在街头看到了一位警察的坚守,感受到了责任的重量。',
44
+ ]
45
+
46
+ arr.forEach((v, i) => {
47
+ data.push({
48
+ _id: `${i + 1}`,
49
+ name: v,
50
+ imgs: [`img${i + 1}`],
51
+ })
52
+
53
+ imports += `import img${i + 1} from '../../public/img/img${i + 1}.jpg';`
54
+ })
55
+
56
+ console.log(data)
57
+ console.log(imports)
58
+ }
59
+
60
+ main()
@@ -0,0 +1,197 @@
1
+ import img1 from '../../public/img/img1.jpg'
2
+ import img2 from '../../public/img/img2.jpg'
3
+ import img3 from '../../public/img/img3.jpg'
4
+ import img4 from '../../public/img/img4.jpg'
5
+ import img5 from '../../public/img/img5.jpg'
6
+ import img6 from '../../public/img/img6.jpg'
7
+ import img7 from '../../public/img/img7.jpg'
8
+ import img8 from '../../public/img/img8.jpg'
9
+ import img9 from '../../public/img/img9.jpg'
10
+ import img10 from '../../public/img/img10.jpg'
11
+ import img11 from '../../public/img/img11.jpg'
12
+ import img12 from '../../public/img/img12.jpg'
13
+ import img13 from '../../public/img/img13.jpg'
14
+ import img14 from '../../public/img/img14.jpg'
15
+ import img15 from '../../public/img/img15.jpg'
16
+ import img16 from '../../public/img/img16.jpg'
17
+ import img17 from '../../public/img/img17.jpg'
18
+ import img18 from '../../public/img/img18.jpg'
19
+ import img19 from '../../public/img/img19.jpg'
20
+ import img20 from '../../public/img/img20.jpg'
21
+ import img21 from '../../public/img/img21.jpg'
22
+ import img22 from '../../public/img/img22.jpg'
23
+ import img23 from '../../public/img/img23.jpg'
24
+ import img24 from '../../public/img/img24.jpg'
25
+ import img25 from '../../public/img/img25.jpg'
26
+ import img26 from '../../public/img/img26.jpg'
27
+ import img27 from '../../public/img/img27.jpg'
28
+ import img28 from '../../public/img/img28.jpg'
29
+ import img29 from '../../public/img/img29.jpg'
30
+ import img30 from '../../public/img/img30.jpg'
31
+ import img31 from '../../public/img/img31.jpg'
32
+ import img32 from '../../public/img/img32.jpg'
33
+ import img33 from '../../public/img/img33.jpg'
34
+ import img34 from '../../public/img/img34.jpg'
35
+ import img35 from '../../public/img/img35.jpg'
36
+ import img36 from '../../public/img/img36.jpg'
37
+ import img37 from '../../public/img/img37.jpg'
38
+ import img38 from '../../public/img/img38.jpg'
39
+
40
+ export const dataConfig = [
41
+ {
42
+ _id: '1',
43
+ name: '今天在公园看到了儿童嬉戏,笑声洒满阳光。',
44
+ imgs: [img1],
45
+ },
46
+ { _id: '2', name: '漫步在街头,感受了都市的繁华与忙碌。', imgs: [img2] },
47
+ { _id: '3', name: '走进书店,被一本历史书籍深深吸引。', imgs: [img3] },
48
+ { _id: '4', name: '在咖啡馆看到了一对恋人的甜蜜。', imgs: [img4] },
49
+ { _id: '5', name: '去了海边,感受到了大海的浩渺与深邃。', imgs: [img5] },
50
+ { _id: '6', name: '在山顶看到了日出,被其壮观所震撼。', imgs: [img6] },
51
+ {
52
+ _id: '7',
53
+ name: '在夜市品尝了各种美食,感受到了人间烟火。',
54
+ imgs: [img7],
55
+ },
56
+ { _id: '8', name: '在画廊欣赏了一幅油画,被其色彩所吸引。', imgs: [img8] },
57
+ {
58
+ _id: '9',
59
+ name: '在博物馆看到了古代的文物,感受到了历史的厚重。',
60
+ imgs: [img9],
61
+ },
62
+ {
63
+ _id: '10',
64
+ name: '在花店看到了各种鲜花,被其美丽所吸引。',
65
+ imgs: [img10],
66
+ },
67
+ {
68
+ _id: '11',
69
+ name: '在街头看到了一只流浪猫,感到了生活的不易。',
70
+ imgs: [img11],
71
+ },
72
+ {
73
+ _id: '12',
74
+ name: '在电影院看了一部电影,被其剧情所吸引。',
75
+ imgs: [img12],
76
+ },
77
+ {
78
+ _id: '13',
79
+ name: '在湖边看到了天鹅的优雅,感受到了大自然的美丽。',
80
+ imgs: [img13],
81
+ },
82
+ {
83
+ _id: '14',
84
+ name: '在公园看到了一位老人的孤独,感到了生活的苍凉。',
85
+ imgs: [img14],
86
+ },
87
+ {
88
+ _id: '15',
89
+ name: '在动物园看到了各种动物,被其独特性格所吸引。',
90
+ imgs: [img15],
91
+ },
92
+ {
93
+ _id: '16',
94
+ name: '在超市看到了各种商品,感受到了生活的丰富。',
95
+ imgs: [img16],
96
+ },
97
+ {
98
+ _id: '17',
99
+ name: '在街头看到了一位街头艺人的表演,被其才华所吸引。',
100
+ imgs: [img17],
101
+ },
102
+ {
103
+ _id: '18',
104
+ name: '在图书馆看到了各种书籍,感受到了知识的深邃。',
105
+ imgs: [img18],
106
+ },
107
+ {
108
+ _id: '19',
109
+ name: '在体育场看到了一场篮球比赛,被其激烈所吸引。',
110
+ imgs: [img19],
111
+ },
112
+ {
113
+ _id: '20',
114
+ name: '在公园看到了春天的繁花,感受到了生命的活力。',
115
+ imgs: [img20],
116
+ },
117
+ {
118
+ _id: '21',
119
+ name: '在音乐会听到了美妙的旋律,被其魅力所吸引。',
120
+ imgs: [img21],
121
+ },
122
+ {
123
+ _id: '22',
124
+ name: '在街头看到了忙碌的人们,感受到了生活的节奏。',
125
+ imgs: [img22],
126
+ },
127
+ {
128
+ _id: '23',
129
+ name: '在公园看到了秋天的落叶,感受到了季节的变迁。',
130
+ imgs: [img23],
131
+ },
132
+ {
133
+ _id: '24',
134
+ name: '在游乐园看到了孩子们的快乐,感受到了童真的美好。',
135
+ imgs: [img24],
136
+ },
137
+ { _id: '25', name: '在山顶看到了星空,被其浩瀚所震撼。', imgs: [img25] },
138
+ { _id: '26', name: '在海边看到了日落,被其美丽所吸引。', imgs: [img26] },
139
+ {
140
+ _id: '27',
141
+ name: '在森林中看到了鸟儿的飞翔,感受到了自由的意义。',
142
+ imgs: [img27],
143
+ },
144
+ {
145
+ _id: '28',
146
+ name: '在湖边看到了鸭子的嬉戏,感受到了生活的乐趣。',
147
+ imgs: [img28],
148
+ },
149
+ {
150
+ _id: '29',
151
+ name: '在街头看到了夜晚的霓虹,感受到了城市的繁华。',
152
+ imgs: [img29],
153
+ },
154
+ { _id: '30', name: '在公园看到了雪景,被其纯净所吸引。', imgs: [img30] },
155
+ {
156
+ _id: '31',
157
+ name: '在图书馆看到了学生的努力,感受到了知识的力量。',
158
+ imgs: [img31],
159
+ },
160
+ {
161
+ _id: '32',
162
+ name: '在咖啡馆看到了一位写作者的专注,被其才华所吸引。',
163
+ imgs: [img32],
164
+ },
165
+ {
166
+ _id: '33',
167
+ name: '在公园看到了一位画家的创作,感受到了艺术的魅力。',
168
+ imgs: [img33],
169
+ },
170
+ {
171
+ _id: '34',
172
+ name: '在街头看到了一位老人的微笑,感受到了生活的温暖。',
173
+ imgs: [img34],
174
+ },
175
+ {
176
+ _id: '35',
177
+ name: '在电影院看到了一部经典电影,被其深度所吸引。',
178
+ imgs: [img35],
179
+ },
180
+ {
181
+ _id: '36',
182
+ name: '在超市看到了一位母亲的辛劳,感受到了母爱的伟大。',
183
+ imgs: [img36],
184
+ },
185
+ {
186
+ _id: '37',
187
+ name: '在公园看到了一位跑步的人,感受到了健康的重要。',
188
+ imgs: [img37],
189
+ },
190
+ {
191
+ _id: '38',
192
+ name: '在街头看到了一位警察的坚守,感受到了责任的重量。',
193
+ imgs: [img38],
194
+ },
195
+ ]
196
+
197
+ export type DataType = (typeof dataConfig)[0]
@@ -0,0 +1 @@
1
+ export * from './data'
@@ -0,0 +1,80 @@
1
+ import { isBrowser } from '@/utils'
2
+ import { useCallback, useEffect, useRef } from 'react'
3
+ import Viewer from 'viewerjs'
4
+
5
+ interface Props {
6
+ /**
7
+ * 预估最大图片数量,用于提前渲染dom
8
+ */
9
+ max: number
10
+ /**
11
+ * viewer配置
12
+ */
13
+ options?: Viewer.Options
14
+ }
15
+ /**
16
+ * 使用viewerjs库展示图集
17
+ */
18
+ export const useImagesViewer = ({ max, options }: Props) => {
19
+ // 单实例单dom,并复用img的dom
20
+ const parentRef = useRef<HTMLDivElement | null>(null)
21
+ const imgArrRef = useRef<HTMLImageElement[]>([])
22
+ const viewerRef = useRef<Viewer | null>(null)
23
+
24
+ // 每次图片变化更新parent下面img的src
25
+ const handleView = useCallback(
26
+ (imgs: string[]) => {
27
+ if (!isBrowser) return
28
+
29
+ const len = imgs.length
30
+
31
+ // 将对应imgs数量的img的dom加载到parent下面
32
+ Array.from({ length: len }).forEach((_, index) => {
33
+ parentRef.current?.appendChild(imgArrRef.current[index])
34
+ })
35
+
36
+ // 加载新imgs
37
+ const childrenArr = Array.from(
38
+ parentRef.current?.children ?? [],
39
+ ) as HTMLImageElement[]
40
+
41
+ childrenArr.forEach((child, index) => {
42
+ child.src = imgs[index]
43
+ })
44
+
45
+ // 更新viewer
46
+ viewerRef.current?.update()
47
+
48
+ // 打开viewer
49
+ viewerRef.current?.show()
50
+ },
51
+ [parentRef.current, imgArrRef.current],
52
+ )
53
+
54
+ // 初始化parent
55
+ useEffect(() => {
56
+ if (!parentRef.current) {
57
+ parentRef.current = document.createElement('div')
58
+ }
59
+ }, [])
60
+
61
+ // 初始化imgArr
62
+ useEffect(() => {
63
+ if (imgArrRef.current.length < max) {
64
+ Array.from({ length: max }).forEach(() => {
65
+ imgArrRef.current.push(new Image())
66
+ })
67
+ }
68
+ }, [max])
69
+
70
+ // 初始化viewer
71
+ useEffect(() => {
72
+ if (parentRef.current) {
73
+ viewerRef.current = new Viewer(parentRef.current, options)
74
+ }
75
+ }, [parentRef.current])
76
+
77
+ return {
78
+ view: handleView,
79
+ }
80
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ /**
4
+ * 获取窗口宽度
5
+ * @description 暂不考虑ssr
6
+ */
7
+ export const useWindowWidth = () => {
8
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth)
9
+
10
+ useEffect(() => {
11
+ const handleResize = () => {
12
+ setWindowWidth(window.innerWidth)
13
+ }
14
+
15
+ window.addEventListener('resize', handleResize)
16
+
17
+ // 清除事件监听器
18
+ return () => {
19
+ window.removeEventListener('resize', handleResize)
20
+ }
21
+ }, [])
22
+
23
+ return windowWidth
24
+ }
@@ -0,0 +1,19 @@
1
+ import { routesConfig } from './routes/config'
2
+ import '@/styles/global.css'
3
+ import 'high-order-ui/lib/index.css'
4
+ import { Suspense } from 'react'
5
+ import { createRoot } from 'react-dom/client'
6
+ import { RouterProvider, createHashRouter } from 'react-router-dom'
7
+ import 'viewerjs/dist/viewer.css'
8
+
9
+ const root = document.getElementById('root')
10
+
11
+ const router = createHashRouter(routesConfig)
12
+
13
+ if (root) {
14
+ createRoot(root).render(
15
+ <Suspense fallback={<div>Loading...</div>}>
16
+ <RouterProvider router={router}></RouterProvider>
17
+ </Suspense>,
18
+ )
19
+ }
@@ -0,0 +1,9 @@
1
+ import { Outlet } from 'react-router-dom'
2
+
3
+ export const MainLayout = () => {
4
+ return (
5
+ <div className="">
6
+ <Outlet></Outlet>
7
+ </div>
8
+ )
9
+ }
@@ -0,0 +1 @@
1
+ export * from './MainLayout'
@@ -0,0 +1,17 @@
1
+ import { routesConfig } from '@/routes/config'
2
+ import { Link } from 'react-router-dom'
3
+
4
+ export default function HomePage() {
5
+ return (
6
+ <div className="flex flex-col">
7
+ {routesConfig[0].children?.map(
8
+ (route) =>
9
+ route.path !== '/' && (
10
+ <Link key={route.path} to={route.path || '/'}>
11
+ {route.path?.slice(1)}
12
+ </Link>
13
+ ),
14
+ )}
15
+ </div>
16
+ )
17
+ }
@@ -0,0 +1,24 @@
1
+ import { dataConfig } from '@/config'
2
+ import { getImageData } from '@/utils'
3
+ import { Image } from 'high-order-ui'
4
+
5
+ export default function ImagePage() {
6
+ const img = dataConfig[0].imgs[0]
7
+ const { width, height, blurhash } = getImageData(img)
8
+
9
+ return (
10
+ <>
11
+ hash占位
12
+ <Image
13
+ width={width}
14
+ height={height}
15
+ blurhash={blurhash}
16
+ src={img}
17
+ ></Image>
18
+ 图片占位
19
+ <Image width={width} height={height} src={img}>
20
+ <div className="w-full h-full bg-gradient-to-r from-purple-500 to-pink-500"></div>
21
+ </Image>
22
+ </>
23
+ )
24
+ }
@@ -0,0 +1,92 @@
1
+ import { DataType, dataConfig } from '@/config'
2
+ import { useImagesViewer } from '@/hooks/useImagesViewer'
3
+ import { useWindowWidth } from '@/hooks/useWindowWidth'
4
+ import { getImageData } from '@/utils'
5
+ import { Image, Masonry } from 'high-order-ui'
6
+ import { FunctionComponent, HtmlHTMLAttributes, memo, useState } from 'react'
7
+
8
+ const initialData = dataConfig.map((i) => {
9
+ const { height, width } = getImageData(i.imgs[0])
10
+
11
+ return {
12
+ data: i,
13
+ itemHeight: ((height ?? 0) / (width ?? 0)) * 100, // 需求精准排列时再计算文字高度
14
+ }
15
+ })
16
+
17
+ export default function MasonryPage() {
18
+ const [data, setData] = useState(initialData)
19
+
20
+ // TODO 使用ResizeObserver api
21
+ const windowWidth = useWindowWidth()
22
+
23
+ const getColumn = (
24
+ windowWidth: number,
25
+ response: [number, number, number],
26
+ ) => {
27
+ if (windowWidth < response[0]) {
28
+ return 2
29
+ } else if (windowWidth < response[1]) {
30
+ return 3
31
+ } else if (windowWidth < response[2]) {
32
+ return 4
33
+ } else {
34
+ return 5
35
+ }
36
+ }
37
+
38
+ return (
39
+ <Masonry
40
+ className="p-[20px] h-screen"
41
+ scrollCallbackRange={700}
42
+ onScrollToFooter={() => {
43
+ setData((pre) => [...pre, ...initialData])
44
+ }}
45
+ items={data}
46
+ renderItem={(item) => (
47
+ <Card key={item._id} className="mb-[15px]" value={item}></Card>
48
+ )}
49
+ columnCount={getColumn(windowWidth, [400, 800, 1200])}
50
+ columnSpace={14}
51
+ ></Masonry>
52
+ )
53
+ }
54
+
55
+ // ---
56
+
57
+ type CardProps = HtmlHTMLAttributes<HTMLDivElement> & {
58
+ value: DataType
59
+ }
60
+
61
+ const Card: FunctionComponent<CardProps> = memo(({ value, ...rest }) => {
62
+ const { height, width, blurhash } = getImageData(value.imgs[0])
63
+
64
+ const { view } = useImagesViewer({
65
+ max: 10,
66
+ options: {
67
+ title: false,
68
+ toggleOnDblclick: false,
69
+ slideOnTouch: false,
70
+ },
71
+ })
72
+
73
+ return (
74
+ <div {...rest}>
75
+ <Image
76
+ onClick={() => {
77
+ view(value.imgs)
78
+ }}
79
+ className="w-full rounded-[16px] mb-[10px] overflow-hidden"
80
+ src={value.imgs[0]}
81
+ alt="封面图"
82
+ width={width}
83
+ height={height}
84
+ blurhash={blurhash}
85
+ ></Image>
86
+
87
+ <span className="text-[#333333] text-[14px] leading-[20px] font-normal">
88
+ {value.name}
89
+ </span>
90
+ </div>
91
+ )
92
+ })
@@ -0,0 +1,36 @@
1
+ import { ScrollView } from 'high-order-ui'
2
+
3
+ export default function ScrollViewPage() {
4
+ return (
5
+ <>
6
+ ↑↓
7
+ <ScrollView
8
+ className="w-full h-[200px]"
9
+ isSmooth
10
+ scrollToPosition={1000}
11
+ onScrollToFooter={() => {
12
+ alert('footer')
13
+ }}
14
+ onScrollToHeader={() => {
15
+ alert('header')
16
+ }}
17
+ >
18
+ <div className="border-4 border-dashed w-full h-[5000px] bg-gradient-to-b from-cyan-500 to-blue-500"></div>
19
+ </ScrollView>
20
+ ←→
21
+ <ScrollView
22
+ scrollDirection="horizontal"
23
+ scrollToPosition={1000}
24
+ onScrollToFooter={() => {
25
+ alert('footer')
26
+ }}
27
+ onScrollToHeader={() => {
28
+ alert('header')
29
+ }}
30
+ className="w-full h-[200px]"
31
+ >
32
+ <div className="border-4 border-dashed w-[5000px] h-full bg-gradient-to-r from-purple-500 to-pink-500"></div>
33
+ </ScrollView>
34
+ </>
35
+ )
36
+ }
@@ -0,0 +1,27 @@
1
+ import { dataConfig } from '@/config'
2
+ import { VirtualList } from 'high-order-ui'
3
+
4
+ export default function VirtualListPage() {
5
+ return (
6
+ <VirtualList
7
+ className="h-screen"
8
+ onScrollToFooter={() => {
9
+ alert('footer')
10
+ }}
11
+ onScrollToHeader={() => {
12
+ alert('header')
13
+ }}
14
+ items={dataConfig.map((item, index) => ({
15
+ data: item,
16
+ itemHeight: 40 + index * 3,
17
+ }))}
18
+ renderItem={(item) => {
19
+ return (
20
+ <div key={item._id} className="border-b border-black h-full">
21
+ {item.name}
22
+ </div>
23
+ )
24
+ }}
25
+ ></VirtualList>
26
+ )
27
+ }
@@ -0,0 +1,39 @@
1
+ import { MainLayout } from '@/layouts'
2
+ import { lazy } from 'react'
3
+ import { RouteObject } from 'react-router-dom'
4
+
5
+ // 使用普通lazy写法 https://github.com/remix-run/react-router/tree/main/examples/lazy-loading,虽然用了RouterProvider,但是RouterProvider写法比较杂乱且有类型风险,暂时不采用https://github.com/remix-run/react-router/issues/10475
6
+ const HomePage = lazy(() => import('@/pages/home'))
7
+ const ImagePage = lazy(() => import('@/pages/image'))
8
+ const MasonryPage = lazy(() => import('@/pages/masonry'))
9
+ const ScrollViewPage = lazy(() => import('@/pages/scroll-view'))
10
+ const VirtualListPage = lazy(() => import('@/pages/virtual-list'))
11
+
12
+ export const routesConfig: RouteObject[] = [
13
+ {
14
+ path: '/',
15
+ element: <MainLayout></MainLayout>,
16
+ children: [
17
+ {
18
+ path: '/',
19
+ element: <HomePage></HomePage>,
20
+ },
21
+ {
22
+ path: '/masonry',
23
+ element: <MasonryPage></MasonryPage>,
24
+ },
25
+ {
26
+ path: '/scroll-view',
27
+ element: <ScrollViewPage></ScrollViewPage>,
28
+ },
29
+ {
30
+ path: '/image',
31
+ element: <ImagePage></ImagePage>,
32
+ },
33
+ {
34
+ path: '/virtual-list',
35
+ element: <VirtualListPage></VirtualListPage>,
36
+ },
37
+ ],
38
+ },
39
+ ]
@@ -0,0 +1,6 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ body {
6
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>examples</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ </body>
12
+ </html>
@@ -0,0 +1,4 @@
1
+ declare module '*.jpg' {
2
+ const value: string
3
+ export = value
4
+ }
@@ -0,0 +1,17 @@
1
+ import queryString from 'query-string'
2
+
3
+ export const isBrowser = !!(
4
+ typeof window !== 'undefined' &&
5
+ window.document &&
6
+ window.document.createElement
7
+ )
8
+
9
+ export const getImageData = (str: string) => {
10
+ const { query } = queryString.parseUrl(str, { decode: false })
11
+
12
+ return {
13
+ width: parseInt(query.width as string),
14
+ height: parseInt(query.height as string),
15
+ blurhash: decodeURIComponent(query.blurhash as string),
16
+ }
17
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: ['./src/**/*.{tsx,ts,jsx,js}'],
4
+ theme: {
5
+ extend: {},
6
+ },
7
+ plugins: [],
8
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ /* ts-node为webpack提供webpack.config.ts的打包产物webpack.config.js https://webpack.js.org/configuration/configuration-languages/*/
3
+ "ts-node": {
4
+ "compilerOptions": {
5
+ "module": "CommonJS"
6
+ }
7
+ },
8
+ "compilerOptions": {
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "jsx": "react-jsx",
12
+ "target": "ESNext",
13
+ "moduleResolution": "Node",
14
+ "resolveJsonModule": true,
15
+ "esModuleInterop": true,
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "@/*": ["./src/*"]
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,94 @@
1
+ import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
2
+ import BlurhashWebpackPlugin from 'blurhash-webpack-plugin'
3
+ import { config } from 'dotenv'
4
+ import HtmlWebpackPlugin from 'html-webpack-plugin'
5
+ import MiniCssExtractPlugin from 'mini-css-extract-plugin'
6
+ import path from 'path'
7
+ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'
8
+ import { DefinePlugin } from 'webpack'
9
+ import { WebpackConfiguration } from 'webpack-dev-server'
10
+
11
+ const isDevelopment = process.env.NODE_ENV !== 'production'
12
+
13
+ const env = config({
14
+ path: path.join(__dirname, `./.env.${process.env.NODE_ENV}`),
15
+ })
16
+
17
+ if (env.error) {
18
+ console.error('环境变量读取失败')
19
+ process.exit(1)
20
+ }
21
+
22
+ // 用WebpackConfiguration是因为配置中配置了devServer
23
+ const webpackConfig: WebpackConfiguration = {
24
+ mode: isDevelopment ? 'development' : 'production',
25
+ entry: path.join(__dirname, './src/index.tsx'),
26
+ output: {
27
+ path: path.join(__dirname, './dist'),
28
+ filename: '[name].[contenthash:8].js',
29
+ chunkFilename: '[id].[contenthash:8].js',
30
+ clean: true,
31
+ hashFunction: 'xxhash64',
32
+ },
33
+ resolve: {
34
+ extensions: ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx'],
35
+ plugins: [new TsconfigPathsPlugin()],
36
+ },
37
+
38
+ optimization: {
39
+ // 最小化 __webpack_require__.u 内容改变的影响,分离webpack runtime文件
40
+ runtimeChunk: {
41
+ name: 'runtime',
42
+ },
43
+ // 分包
44
+ splitChunks: {
45
+ chunks: 'all',
46
+ },
47
+ },
48
+ module: {
49
+ rules: [
50
+ {
51
+ test: /\.css$/,
52
+ use: [
53
+ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
54
+ 'css-loader',
55
+ 'postcss-loader',
56
+ ],
57
+ },
58
+ {
59
+ test: /\.(jsx?|mjs|cjs|tsx?)$/,
60
+ exclude: /node_modules/,
61
+ include: path.join(__dirname, 'src'),
62
+ use: ['babel-loader', 'thread-loader'],
63
+ },
64
+ {
65
+ test: /\.(png|jpe?g|gif|svg|webp)$/,
66
+ type: 'asset/resource',
67
+ },
68
+ ],
69
+ },
70
+ plugins: [
71
+ new DefinePlugin({ 'process.env': JSON.stringify(env.parsed) }),
72
+ new HtmlWebpackPlugin({
73
+ template: path.join(__dirname, './src/templates/index.html'),
74
+ }),
75
+ new MiniCssExtractPlugin({
76
+ filename: '[name].[contenthash:8].css',
77
+ }),
78
+ isDevelopment && new ReactRefreshWebpackPlugin(),
79
+ new BlurhashWebpackPlugin(),
80
+ ].filter(Boolean),
81
+ devServer: {
82
+ static: {
83
+ directory: path.join(__dirname, './public'), //托管静态资源public文件夹
84
+ },
85
+ port: 9001,
86
+ compress: false,
87
+ headers: {
88
+ 'Access-Control-Allow-Origin': '*',
89
+ },
90
+ historyApiFallback: true,
91
+ },
92
+ }
93
+
94
+ export default webpackConfig
@@ -1,12 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "skipLibCheck": true,
5
- "noEmit": true,
6
- "module": "ESNext",
7
- "target": "ESNext",
8
- "resolveJsonModule": true,
9
- "moduleResolution": "node",
10
- "esModuleInterop": true
11
- }
12
- }