@blocklet/pages-kit 0.4.109 → 0.4.111
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.
- package/lib/cjs/builtin/color-picker.js +224 -0
- package/lib/cjs/builtin/uploader.js +74 -0
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/utils/builtin.js +2 -0
- package/lib/cjs/utils/inject-global-components.js +5 -1
- package/lib/cjs/utils/style.js +4 -1
- package/lib/esm/builtin/color-picker.js +193 -0
- package/lib/esm/builtin/uploader.js +64 -0
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/utils/builtin.js +2 -0
- package/lib/esm/utils/inject-global-components.js +5 -1
- package/lib/esm/utils/style.js +4 -1
- package/lib/types/builtin/color-picker.d.ts +26 -0
- package/lib/types/builtin/uploader.d.ts +18 -0
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/types/style.d.ts +2 -1
- package/lib/types/utils/builtin.d.ts +2 -0
- package/lib/types/utils/style.d.ts +1 -1
- package/package.json +9 -4
package/lib/cjs/utils/builtin.js
CHANGED
|
@@ -72,4 +72,6 @@ exports.BuiltinModules = {
|
|
|
72
72
|
'@blocklet/pages-kit/builtin/async/react-syntax-highlighter': {},
|
|
73
73
|
'@blocklet/pages-kit/builtin/async/image-preview': {},
|
|
74
74
|
'@blocklet/pages-kit/builtin/async/ai-runtime': {},
|
|
75
|
+
'@blocklet/pages-kit/builtin/uploader': {},
|
|
76
|
+
'@blocklet/pages-kit/builtin/color-picker': {},
|
|
75
77
|
};
|
|
@@ -35,6 +35,7 @@ const imagePreview = __importStar(require("../builtin/async/image-preview"));
|
|
|
35
35
|
const reactMarkdown = __importStar(require("../builtin/async/react-markdown"));
|
|
36
36
|
const reactScrollToBottom = __importStar(require("../builtin/async/react-scroll-to-bottom"));
|
|
37
37
|
const reactSyntaxHighlighter = __importStar(require("../builtin/async/react-syntax-highlighter"));
|
|
38
|
+
const colorPicker = __importStar(require("../builtin/color-picker"));
|
|
38
39
|
const components = __importStar(require("../builtin/components"));
|
|
39
40
|
const dayjs = __importStar(require("../builtin/dayjs"));
|
|
40
41
|
const emotionCss = __importStar(require("../builtin/emotion/css"));
|
|
@@ -51,6 +52,7 @@ const reactRouterDOM = __importStar(require("../builtin/react-router-dom"));
|
|
|
51
52
|
const reactWrapBalancer = __importStar(require("../builtin/react-wrap-balancer"));
|
|
52
53
|
const session = __importStar(require("../builtin/session"));
|
|
53
54
|
const stream = __importStar(require("../builtin/stream"));
|
|
55
|
+
const uploader = __importStar(require("../builtin/uploader"));
|
|
54
56
|
const utils = __importStar(require("../builtin/utils"));
|
|
55
57
|
const zustand = __importStar(require("../builtin/zustand"));
|
|
56
58
|
const zustandMiddlewareImmer = __importStar(require("../builtin/zustand/middleware/immer"));
|
|
@@ -80,6 +82,9 @@ function injectGlobalComponents() {
|
|
|
80
82
|
'@blocklet/pages-kit/builtin/utils': utils,
|
|
81
83
|
'@blocklet/pages-kit/builtin/stream': stream,
|
|
82
84
|
'@blocklet/pages-kit/builtin/react': react,
|
|
85
|
+
'@blocklet/pages-kit/builtin/react-dom': reactDOM,
|
|
86
|
+
'@blocklet/pages-kit/builtin/uploader': uploader,
|
|
87
|
+
'@blocklet/pages-kit/builtin/color-picker': colorPicker,
|
|
83
88
|
'@blocklet/pages-kit/builtin/emotion/css': emotionCss,
|
|
84
89
|
'@blocklet/pages-kit/builtin/mui/material': muiMaterial,
|
|
85
90
|
'@blocklet/pages-kit/builtin/mui/lab': muiLab,
|
|
@@ -90,7 +95,6 @@ function injectGlobalComponents() {
|
|
|
90
95
|
'@blocklet/pages-kit/builtin/immer': immer,
|
|
91
96
|
'@blocklet/pages-kit/builtin/react-wrap-balancer': reactWrapBalancer,
|
|
92
97
|
'@blocklet/pages-kit/builtin/react-router-dom': reactRouterDOM,
|
|
93
|
-
'@blocklet/pages-kit/builtin/react-dom': reactDOM,
|
|
94
98
|
'@blocklet/pages-kit/builtin/session': session,
|
|
95
99
|
'@blocklet/pages-kit/builtin/locale': locale,
|
|
96
100
|
'@blocklet/pages-kit/builtin/arcblock/ux': arcblockUx,
|
package/lib/cjs/utils/style.js
CHANGED
|
@@ -155,7 +155,10 @@ function parseColor(color = '') {
|
|
|
155
155
|
*/
|
|
156
156
|
function colorConvert(color, _theme = blockletTheme) {
|
|
157
157
|
if (isMuiColorKey(color)) {
|
|
158
|
-
|
|
158
|
+
if (color === 'transparent') {
|
|
159
|
+
return 'transparent';
|
|
160
|
+
}
|
|
161
|
+
return (0, get_1.default)(_theme.palette, color, color);
|
|
159
162
|
}
|
|
160
163
|
// 处理渐变和普通颜色
|
|
161
164
|
if (isGradient(color)) {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Empty from '@arcblock/ux/lib/Empty';
|
|
3
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
|
+
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, ButtonGroup, Typography, Tooltip, Skeleton, } from '@mui/material';
|
|
5
|
+
import { useBoolean, useLocalStorageState, useReactive } from 'ahooks';
|
|
6
|
+
import { Suspense, forwardRef, lazy, useCallback, useImperativeHandle, useRef } from 'react';
|
|
7
|
+
import tinycolor from 'tinycolor2';
|
|
8
|
+
import { useMuiColorPalette } from '../contexts/color';
|
|
9
|
+
import { isColorString, isGradient, getSafeGradient } from '../utils/style';
|
|
10
|
+
const ColorPickerLib = lazy(() => import('react-best-gradient-color-picker'));
|
|
11
|
+
const HEIGHT = 400;
|
|
12
|
+
const WIDTH = 270;
|
|
13
|
+
export function ColorItem({ color, sx = {}, ...props }) {
|
|
14
|
+
isColorString(color);
|
|
15
|
+
const styleMap = {};
|
|
16
|
+
if (isGradient(color)) {
|
|
17
|
+
styleMap.backgroundImage = color;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
styleMap.backgroundColor = color;
|
|
21
|
+
}
|
|
22
|
+
return (_jsx(Box, { sx: {
|
|
23
|
+
...sx,
|
|
24
|
+
width: '100%',
|
|
25
|
+
paddingBottom: '100%',
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
transition: 'opacity 0.2s',
|
|
28
|
+
border: '1px solid #ccc',
|
|
29
|
+
borderRadius: 0.5,
|
|
30
|
+
'&:hover': {
|
|
31
|
+
opacity: 0.75,
|
|
32
|
+
},
|
|
33
|
+
...styleMap,
|
|
34
|
+
}, ...props }));
|
|
35
|
+
}
|
|
36
|
+
export function ColorPicker({ value, onChange, style = {}, sx = {} }) {
|
|
37
|
+
const refDialog = useRef(null);
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsx(ColorItem, { color: value, style: { width: '1rem', height: '1rem', padding: 0, marginRight: '0.25rem', ...style }, sx: { ...sx }, onClick: () => {
|
|
39
|
+
refDialog.current?.open({ value });
|
|
40
|
+
} }), _jsx(ConfigColorDialog, { ref: refDialog, onSave: ({ value }, close) => {
|
|
41
|
+
onChange(value);
|
|
42
|
+
close();
|
|
43
|
+
} })] }));
|
|
44
|
+
}
|
|
45
|
+
export default ColorPicker;
|
|
46
|
+
export const ConfigColorDialog = forwardRef(function ConfigColorDialog({ onSave, enableMuiPalette = true }, ref) {
|
|
47
|
+
const state = useReactive({
|
|
48
|
+
value: '',
|
|
49
|
+
originalMuiKey: null,
|
|
50
|
+
});
|
|
51
|
+
const { t } = useLocaleContext();
|
|
52
|
+
const [activeTab, setActiveTab] = useLocalStorageState('pages-kit:color-picker-active-tab', {
|
|
53
|
+
defaultValue: 0,
|
|
54
|
+
});
|
|
55
|
+
const muiPalette = useMuiColorPalette();
|
|
56
|
+
const previewStyle = {};
|
|
57
|
+
if (state.value) {
|
|
58
|
+
if (isGradient(state.value)) {
|
|
59
|
+
previewStyle.backgroundImage = state.value;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
previewStyle.backgroundColor = state.value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
previewStyle.border = '1px solid #ccc';
|
|
67
|
+
}
|
|
68
|
+
const [open, { setFalse, setTrue }] = useBoolean(false);
|
|
69
|
+
const handleClose = useCallback(() => {
|
|
70
|
+
setFalse();
|
|
71
|
+
}, [setFalse]);
|
|
72
|
+
const isActiveMuiTab = enableMuiPalette && activeTab === 0;
|
|
73
|
+
useImperativeHandle(ref, () => {
|
|
74
|
+
return {
|
|
75
|
+
open({ value } = { value: '' }) {
|
|
76
|
+
// 处理 MUI 色板键
|
|
77
|
+
if (enableMuiPalette && muiPalette.isMuiColorKey(value)) {
|
|
78
|
+
state.originalMuiKey = value;
|
|
79
|
+
const colorValue = muiPalette.getColorByMuiKey(value);
|
|
80
|
+
state.value = getSafeGradient(colorValue || value);
|
|
81
|
+
// 如果是 MUI 颜色键,再切换到 MUI Theme 标签
|
|
82
|
+
setActiveTab(0);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
state.originalMuiKey = null;
|
|
86
|
+
state.value = getSafeGradient(value);
|
|
87
|
+
// 不自动切换标签,尊重用户上一次的选择
|
|
88
|
+
// 只有在 MUI 不可用时才强制切换到自定义
|
|
89
|
+
if (!enableMuiPalette) {
|
|
90
|
+
setActiveTab(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
setTrue();
|
|
94
|
+
},
|
|
95
|
+
close() {
|
|
96
|
+
handleClose();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
const handleSave = () => {
|
|
101
|
+
// 启用 MUI 预设时,检查是否为 MUI 颜色
|
|
102
|
+
if (enableMuiPalette && muiPalette) {
|
|
103
|
+
// 只有当前在 MUI Theme 标签页时才尝试自动转换为 MUI 色键
|
|
104
|
+
if (isActiveMuiTab) {
|
|
105
|
+
const muiKey = muiPalette.getMuiKeyByColor(state.value);
|
|
106
|
+
onSave({ value: muiKey || state.value }, handleClose);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// 在自定义色彩标签页时,保持原始值
|
|
110
|
+
onSave({ value: state.value }, handleClose);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
onSave({ value: state.value }, handleClose);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
// 直接从 hook 获取分组
|
|
118
|
+
const { groupedMuiColors } = muiPalette;
|
|
119
|
+
return (_jsxs(Dialog, { disableEnforceFocus: true, open: open, onClose: handleClose, children: [_jsxs(DialogTitle, { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(Typography, { variant: "inherit", component: "div", children: t('maker.configColor') }), enableMuiPalette && (_jsxs(ButtonGroup, { size: "small", "aria-label": "color mode selector", children: [_jsx(Button, { variant: isActiveMuiTab ? 'contained' : 'outlined', onClick: () => setActiveTab(0), sx: {
|
|
120
|
+
fontWeight: 500,
|
|
121
|
+
textTransform: 'none',
|
|
122
|
+
minWidth: 'unset',
|
|
123
|
+
}, children: t('maker.configColorMuiTheme') || 'Theme Color' }), _jsx(Button, { variant: activeTab === 1 ? 'contained' : 'outlined', onClick: () => setActiveTab(1), sx: {
|
|
124
|
+
fontWeight: 500,
|
|
125
|
+
textTransform: 'none',
|
|
126
|
+
minWidth: 'unset',
|
|
127
|
+
}, children: t('maker.configColorCustomColor') || 'Custom Color' })] }))] }), _jsxs(DialogContent, { sx: { width: 600, display: 'flex', flexDirection: 'row', gap: 2, overflowX: 'hidden' }, children: [_jsxs(Box, { sx: {
|
|
128
|
+
...previewStyle,
|
|
129
|
+
flex: '0 0 270px',
|
|
130
|
+
borderRadius: 1,
|
|
131
|
+
display: 'flex',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
justifyContent: 'center',
|
|
134
|
+
position: 'sticky',
|
|
135
|
+
top: 0,
|
|
136
|
+
}, children: [!state.value && _jsx(Empty, { children: t('error.notConfig') }), state.originalMuiKey && isActiveMuiTab && (_jsx(Typography, { variant: "body2", sx: {
|
|
137
|
+
bgcolor: 'rgba(255,255,255,0.7)',
|
|
138
|
+
px: 2,
|
|
139
|
+
py: 1,
|
|
140
|
+
borderRadius: 1,
|
|
141
|
+
color: '#000',
|
|
142
|
+
maxWidth: '90%',
|
|
143
|
+
textAlign: 'center',
|
|
144
|
+
}, children: state.originalMuiKey }))] }, "color-preview"), _jsx(Box, { sx: { flex: 1 }, children: isActiveMuiTab ? (
|
|
145
|
+
// MUI 主题色面板
|
|
146
|
+
_jsx(Box, { sx: { height: HEIGHT }, children: Object.entries(groupedMuiColors).map(([palette, paletteColors]) => (_jsxs(Box, { sx: { pb: 1.5 }, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, sx: { textTransform: 'capitalize', mb: 0.5, lineHeight: 1 }, children: palette }), _jsx(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1 }, children: paletteColors.map((item) => {
|
|
147
|
+
let selectedColor = '';
|
|
148
|
+
if (state.originalMuiKey === item.colorKey) {
|
|
149
|
+
if (item.colorValue === 'transparent') {
|
|
150
|
+
selectedColor = 'rgba(0,0,0,0.7)';
|
|
151
|
+
}
|
|
152
|
+
else if (tinycolor(item.colorValue).isDark()) {
|
|
153
|
+
selectedColor = 'rgba(255,255,255,0.8)';
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
selectedColor = 'rgba(0,0,0,0.7)';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return (_jsx(Tooltip, { title: item.colorKey, children: _jsx(Box, { onClick: () => {
|
|
160
|
+
state.value = item.colorValue;
|
|
161
|
+
state.originalMuiKey = item.colorKey;
|
|
162
|
+
}, sx: {
|
|
163
|
+
width: 28,
|
|
164
|
+
height: 28,
|
|
165
|
+
bgcolor: item.colorValue,
|
|
166
|
+
border: state.originalMuiKey === item.colorKey
|
|
167
|
+
? `2px solid ${selectedColor}`
|
|
168
|
+
: '1px solid #ccc',
|
|
169
|
+
borderRadius: 0.5,
|
|
170
|
+
cursor: 'pointer',
|
|
171
|
+
'&:hover': { opacity: 0.8 },
|
|
172
|
+
display: 'flex',
|
|
173
|
+
alignItems: 'center',
|
|
174
|
+
justifyContent: 'center',
|
|
175
|
+
// 透明色时显示棋盘背景
|
|
176
|
+
...(item.colorValue === 'transparent'
|
|
177
|
+
? {
|
|
178
|
+
background: 'repeating-conic-gradient(#CCCCCC 0% 25%, #FFFFFF 0% 50%) 50% / 10px 10px',
|
|
179
|
+
}
|
|
180
|
+
: {}),
|
|
181
|
+
}, children: state.originalMuiKey === item.colorKey && (_jsx("i", { className: "i-mdi:check", style: {
|
|
182
|
+
color: selectedColor,
|
|
183
|
+
fontSize: '1.2rem',
|
|
184
|
+
} })) }) }, item.colorKey));
|
|
185
|
+
}) })] }, palette))) })) : (
|
|
186
|
+
// 自定义颜色选择器
|
|
187
|
+
_jsx(Box, { sx: { overflowY: 'auto', overflowX: 'hidden' }, children: _jsx(Suspense, { fallback: _jsx(Skeleton, { variant: "rectangular", width: WIDTH, height: HEIGHT }), children: _jsx(ColorPickerLib, { disableDarkMode: true, width: WIDTH,
|
|
188
|
+
// menu color picker height
|
|
189
|
+
height: WIDTH, value: state.value, onChange: (value) => {
|
|
190
|
+
state.value = value;
|
|
191
|
+
state.originalMuiKey = null;
|
|
192
|
+
}, hidePresets: true, hideAdvancedSliders: true, hideColorGuide: true, hideInputType: true }) }) })) })] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "outlined", size: "small", onClick: handleClose, children: t('common.cancel') }), _jsx(Button, { variant: "contained", size: "small", onClick: handleSave, children: t('maker.save') })] })] }));
|
|
193
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { UploaderProvider as UploaderProviderLib, useUploaderContext } from '@blocklet/uploader/react';
|
|
3
|
+
import UploadIcon from '@mui/icons-material/Upload';
|
|
4
|
+
import { IconButton } from '@mui/material';
|
|
5
|
+
export const useUploader = useUploaderContext;
|
|
6
|
+
export function UploaderButton({ onChange }) {
|
|
7
|
+
const uploaderRef = useUploader();
|
|
8
|
+
const handleOpen = () => {
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
const uploader = uploaderRef?.current?.getUploader();
|
|
11
|
+
uploader?.open();
|
|
12
|
+
if (onChange) {
|
|
13
|
+
// rewrite default emitter
|
|
14
|
+
uploader.onceUploadSuccess((...args) => {
|
|
15
|
+
onChange(...args);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
return (_jsx(IconButton, { size: "small", onClick: handleOpen, children: _jsx(UploadIcon, {}) }, "uploader-trigger"));
|
|
20
|
+
}
|
|
21
|
+
export function UploaderProvider({ children }) {
|
|
22
|
+
return (_jsx(UploaderProviderLib, { popup: true,
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
coreProps: {
|
|
25
|
+
restrictions: {
|
|
26
|
+
allowedFileTypes: [
|
|
27
|
+
'image/png',
|
|
28
|
+
'image/jpeg',
|
|
29
|
+
'image/gif',
|
|
30
|
+
'image/webp',
|
|
31
|
+
'image/svg+xml',
|
|
32
|
+
'video/mp4',
|
|
33
|
+
'video/webm',
|
|
34
|
+
'audio/mpeg',
|
|
35
|
+
],
|
|
36
|
+
maxNumberOfFiles: 1,
|
|
37
|
+
},
|
|
38
|
+
}, children: children }, "uploader"
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
export default UploaderProvider;
|
|
43
|
+
export function getVideoSize(url) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const video = document.createElement('video');
|
|
46
|
+
video.src = url;
|
|
47
|
+
video.onloadedmetadata = () => {
|
|
48
|
+
const { videoWidth: naturalWidth, videoHeight: naturalHeight } = video;
|
|
49
|
+
resolve({ naturalWidth, naturalHeight });
|
|
50
|
+
};
|
|
51
|
+
video.onerror = (e) => reject(e);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export function getImageSize(url) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const img = new Image();
|
|
57
|
+
img.src = url;
|
|
58
|
+
img.onload = () => {
|
|
59
|
+
const { naturalWidth, naturalHeight } = img;
|
|
60
|
+
resolve({ naturalWidth, naturalHeight });
|
|
61
|
+
};
|
|
62
|
+
img.onerror = (e) => reject(e);
|
|
63
|
+
});
|
|
64
|
+
}
|