lupine.components 1.0.10 → 1.0.12
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/package.json +1 -1
- package/src/components/index.ts +2 -0
- package/src/components/mobile-components/index.ts +2 -0
- package/src/components/mobile-components/mobile-footer-menu.tsx +99 -0
- package/src/components/mobile-components/mobile-header-component.tsx +89 -0
- package/src/components/popup-menu.tsx +69 -9
- package/src/frames/desktop-frame.tsx +80 -0
- package/src/frames/header-with-back-frame.tsx +117 -0
- package/src/frames/index.ts +5 -0
- package/src/frames/responsive-frame.tsx +85 -0
- package/src/frames/slider-frame.tsx +99 -0
- package/src/frames/top-frame.tsx +28 -0
- package/src/index.ts +2 -1
- package/src/styles/dark-themes.ts +12 -1
- package/src/styles/light-themes.ts +11 -0
package/package.json
CHANGED
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { CssProps } from 'lupine.web';
|
|
2
|
+
import { MediaQueryRange } from '../../styles';
|
|
3
|
+
|
|
4
|
+
export interface MobileFooterMenuItemProps {
|
|
5
|
+
icon: string;
|
|
6
|
+
href: string;
|
|
7
|
+
text: string;
|
|
8
|
+
topout?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MobileFooterMenuProps {
|
|
11
|
+
items: MobileFooterMenuItemProps[];
|
|
12
|
+
color?: string;
|
|
13
|
+
activeColor?: string;
|
|
14
|
+
topoutColor?: string;
|
|
15
|
+
topoutBackgroundColor?: string;
|
|
16
|
+
}
|
|
17
|
+
export const MobileFooterMenu = (props: MobileFooterMenuProps) => {
|
|
18
|
+
const css: CssProps = {
|
|
19
|
+
'.footer-menu': {
|
|
20
|
+
display: 'none',
|
|
21
|
+
// position: 'fixed',
|
|
22
|
+
// left: 0,
|
|
23
|
+
// right: 0,
|
|
24
|
+
// bottom: 0,
|
|
25
|
+
width: '100%',
|
|
26
|
+
background: 'var(--sidebar-bg-color)',
|
|
27
|
+
paddingBottom: 'env(safe-area-inset-bottom)',
|
|
28
|
+
minHeight: '50px',
|
|
29
|
+
justifyContent: 'space-around',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
borderTop: 'var(--primary-border)',
|
|
32
|
+
},
|
|
33
|
+
'.footer-menu, .footer-menu a': {
|
|
34
|
+
textDecoration: 'none',
|
|
35
|
+
color: props.color || 'var(--primary-color)',
|
|
36
|
+
},
|
|
37
|
+
'.footer-menu .footer-menu-item': {
|
|
38
|
+
padding: '4px 16px 4px 16px',
|
|
39
|
+
fontSize: '11px',
|
|
40
|
+
height: '55px', // 和主页保留的底部菜单高度一致
|
|
41
|
+
width: '55px',
|
|
42
|
+
display: 'flex',
|
|
43
|
+
flexWrap: 'wrap',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
},
|
|
46
|
+
'.footer-menu .footer-menu-item i': {
|
|
47
|
+
display: 'block',
|
|
48
|
+
fontSize: '22px',
|
|
49
|
+
marginBottom: '4px',
|
|
50
|
+
},
|
|
51
|
+
'.footer-menu .footer-menu-item.footer-menu-topout': {
|
|
52
|
+
marginTop: '-43px',
|
|
53
|
+
borderRadius: '50%',
|
|
54
|
+
backgroundColor: props.topoutBackgroundColor || '#ff8f8f',
|
|
55
|
+
color: props.topoutColor || 'var(--primary-color)',
|
|
56
|
+
zIndex: 'var(--layer-header-footer)',
|
|
57
|
+
},
|
|
58
|
+
'.footer-menu .footer-menu-item.active': {
|
|
59
|
+
color: props.activeColor || 'var(--primary-accent-color)',
|
|
60
|
+
},
|
|
61
|
+
[MediaQueryRange.TabletBelow]: {
|
|
62
|
+
'.footer-menu': {
|
|
63
|
+
display: 'flex',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const onClick = (index: number, href: string) => {
|
|
68
|
+
const items = document.querySelector('.footer-menu-item.active');
|
|
69
|
+
items?.classList.remove('active');
|
|
70
|
+
// find footer-menu-item by index
|
|
71
|
+
const item = document.querySelector(`a:nth-child(${index + 1}) .footer-menu-item`);
|
|
72
|
+
item?.classList.add('active');
|
|
73
|
+
};
|
|
74
|
+
let curretnUrl = typeof window !== 'undefined' ? window.location.pathname : '';
|
|
75
|
+
return (
|
|
76
|
+
<div css={css} class='footer-menu-box'>
|
|
77
|
+
<div class='footer-menu'>
|
|
78
|
+
{props.items.map((item, index) => (
|
|
79
|
+
<a href={item.href} key={index}>
|
|
80
|
+
<div
|
|
81
|
+
class={`footer-menu-item ${item.topout ? 'footer-menu-topout' : ''} ${
|
|
82
|
+
curretnUrl === item.href ? 'active' : ''
|
|
83
|
+
}`}
|
|
84
|
+
onClick={() => onClick(index, item.href)}
|
|
85
|
+
>
|
|
86
|
+
<i class={`ifc-icon ${item.icon}`}></i>
|
|
87
|
+
{item.text}
|
|
88
|
+
</div>
|
|
89
|
+
</a>
|
|
90
|
+
))}
|
|
91
|
+
{/* <div class='footer-menu-item'><a href='/'><i class="ifc-icon bs-person-gear"></i>主页</a></div>
|
|
92
|
+
<div class='footer-menu-item'><a href='/user/tools'><i class="ifc-icon bo-multimedia-music-note"></i>工具</a></div>
|
|
93
|
+
<div class='footer-menu-item footer-menu-topout'><a href='/user/customer'><i class="ifc-icon co-cil-chat-bubble"></i>客服</a></div>
|
|
94
|
+
<div class='footer-menu-item'><a href='/user/member'><i class="ifc-icon ma-crown-outline"></i>会员</a></div>
|
|
95
|
+
<div class='footer-menu-item'><a href='/user/mine'><i class="ifc-icon bs-person-gear"></i>我的</a></div> */}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CssProps, VNode } from 'lupine.web';
|
|
2
|
+
import { HtmlVar } from '../html-var';
|
|
3
|
+
|
|
4
|
+
export class MobileHeaderHelper {
|
|
5
|
+
private static instance: MobileHeaderHelper;
|
|
6
|
+
|
|
7
|
+
private leftContent = HtmlVar('');
|
|
8
|
+
private centerContent = HtmlVar('');
|
|
9
|
+
private rightContent = HtmlVar('');
|
|
10
|
+
private constructor() {}
|
|
11
|
+
|
|
12
|
+
public static getInstance(): MobileHeaderHelper {
|
|
13
|
+
if (!MobileHeaderHelper.instance) {
|
|
14
|
+
MobileHeaderHelper.instance = new MobileHeaderHelper();
|
|
15
|
+
}
|
|
16
|
+
return MobileHeaderHelper.instance;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public setLeftContent(content: string | VNode<any>) {
|
|
20
|
+
this.leftContent.value = content;
|
|
21
|
+
}
|
|
22
|
+
public getLeftContent() {
|
|
23
|
+
return this.leftContent;
|
|
24
|
+
}
|
|
25
|
+
public setCenterContent(content: string | VNode<any>) {
|
|
26
|
+
this.centerContent.value = content;
|
|
27
|
+
}
|
|
28
|
+
public getCenterContent() {
|
|
29
|
+
return this.centerContent;
|
|
30
|
+
}
|
|
31
|
+
public setRightContent(content: string | VNode<any>) {
|
|
32
|
+
this.rightContent.value = content;
|
|
33
|
+
}
|
|
34
|
+
public getRightContent() {
|
|
35
|
+
return this.rightContent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public hideHeader() {
|
|
39
|
+
this.leftContent.value = '';
|
|
40
|
+
this.centerContent.value = '';
|
|
41
|
+
this.rightContent.value = '';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const mobileHeaderHelper = MobileHeaderHelper.getInstance();
|
|
45
|
+
|
|
46
|
+
export const MobileHeaderLeft = (props: { children: VNode<any> }) => {
|
|
47
|
+
mobileHeaderHelper.setLeftContent(props.children);
|
|
48
|
+
return <></>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const MobileHeaderCenter = (props: { children: VNode<any> }) => {
|
|
52
|
+
mobileHeaderHelper.setCenterContent(props.children);
|
|
53
|
+
return <></>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const MobileHeaderRight = (props: { children: VNode<any> }) => {
|
|
57
|
+
mobileHeaderHelper.setRightContent(props.children);
|
|
58
|
+
return <></>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const MobileHeaderHide = () => {
|
|
62
|
+
mobileHeaderHelper.hideHeader();
|
|
63
|
+
return <></>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const MobileHeaderComponent = (props: any) => {
|
|
67
|
+
const css: CssProps = {
|
|
68
|
+
display: 'flex',
|
|
69
|
+
flexDirection: 'row',
|
|
70
|
+
width: '100%',
|
|
71
|
+
height: 'auto',
|
|
72
|
+
padding: '2px 0',
|
|
73
|
+
boxShadow: '0 4px 4px var(--primary-border-color)', // 第二项 2px 的话,顶部有阴影(线)
|
|
74
|
+
'& > *': {
|
|
75
|
+
height: '100%',
|
|
76
|
+
},
|
|
77
|
+
'.mobile-header-center': {
|
|
78
|
+
flex: 1,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div css={css} class='mobile-header-component'>
|
|
84
|
+
<div class='mobile-header-left'>{mobileHeaderHelper.getLeftContent().node}</div>
|
|
85
|
+
<div class='mobile-header-center'>{mobileHeaderHelper.getCenterContent().node}</div>
|
|
86
|
+
<div class='mobile-header-right'>{mobileHeaderHelper.getRightContent().node}</div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
@@ -1,11 +1,46 @@
|
|
|
1
|
-
import { RefProps } from 'lupine.web';
|
|
1
|
+
import { CssProps, RefProps, VNode } from 'lupine.web';
|
|
2
2
|
import { stopPropagation } from '../lib';
|
|
3
3
|
|
|
4
4
|
export type PopupMenuOpenMenuProps = { handle?: Function };
|
|
5
5
|
|
|
6
|
+
// defaultValue=<i class='ifc-icon co-cil-hamburger-menu'></i>
|
|
7
|
+
export const PopupMenuWithIcon = (props: PopupMenuProps) => {
|
|
8
|
+
const handle: PopupMenuOpenMenuProps = {};
|
|
9
|
+
const css: CssProps = {
|
|
10
|
+
cursor: 'pointer',
|
|
11
|
+
display: 'flex',
|
|
12
|
+
flexDirection: 'row',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
fontSize: '24px',
|
|
15
|
+
};
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
onClick={() => {
|
|
19
|
+
handle.handle && handle.handle();
|
|
20
|
+
}}
|
|
21
|
+
css={css}
|
|
22
|
+
>
|
|
23
|
+
<PopupMenu
|
|
24
|
+
list={props.list}
|
|
25
|
+
defaultValue={props.defaultValue}
|
|
26
|
+
tips={props.tips}
|
|
27
|
+
minWidth={props.minWidth}
|
|
28
|
+
maxWidth={props.maxWidth}
|
|
29
|
+
maxHeight={props.maxHeight}
|
|
30
|
+
handleSelected={props.handleSelected}
|
|
31
|
+
handleOpened={props.handleOpened}
|
|
32
|
+
handleClosed={props.handleClosed}
|
|
33
|
+
noUpdateValue={props.noUpdateValue}
|
|
34
|
+
refOpenMenu={handle}
|
|
35
|
+
noTriangleIcon={props.noTriangleIcon}
|
|
36
|
+
></PopupMenu>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
6
41
|
export type PopupMenuProps = {
|
|
7
42
|
list: string[];
|
|
8
|
-
defaultValue: string
|
|
43
|
+
defaultValue: string | VNode<any>;
|
|
9
44
|
tips?: string;
|
|
10
45
|
minWidth?: string;
|
|
11
46
|
maxWidth?: string;
|
|
@@ -15,6 +50,8 @@ export type PopupMenuProps = {
|
|
|
15
50
|
handleClosed?: Function;
|
|
16
51
|
noUpdateValue?: boolean;
|
|
17
52
|
refOpenMenu?: PopupMenuOpenMenuProps;
|
|
53
|
+
noTriangleIcon?: boolean;
|
|
54
|
+
align?: 'left' | 'right';
|
|
18
55
|
};
|
|
19
56
|
|
|
20
57
|
export type PopupMenuWithButtonProps = { label: string } & PopupMenuProps;
|
|
@@ -42,6 +79,8 @@ export const PopupMenuWithButton = (props: PopupMenuWithButtonProps) => {
|
|
|
42
79
|
handleClosed={props.handleClosed}
|
|
43
80
|
noUpdateValue={props.noUpdateValue}
|
|
44
81
|
refOpenMenu={handle}
|
|
82
|
+
noTriangleIcon={props.noTriangleIcon}
|
|
83
|
+
align={props.align}
|
|
45
84
|
></PopupMenu>
|
|
46
85
|
</button>
|
|
47
86
|
);
|
|
@@ -71,6 +110,8 @@ export const PopupMenuWithLabel = (props: PopupMenuWithLabelProps) => {
|
|
|
71
110
|
handleClosed={props.handleClosed}
|
|
72
111
|
noUpdateValue={props.noUpdateValue}
|
|
73
112
|
refOpenMenu={handle}
|
|
113
|
+
noTriangleIcon={props.noTriangleIcon}
|
|
114
|
+
align={props.align}
|
|
74
115
|
></PopupMenu>
|
|
75
116
|
</div>
|
|
76
117
|
);
|
|
@@ -88,20 +129,22 @@ export const PopupMenu = ({
|
|
|
88
129
|
handleClosed,
|
|
89
130
|
noUpdateValue,
|
|
90
131
|
refOpenMenu,
|
|
132
|
+
align = 'right',
|
|
133
|
+
noTriangleIcon,
|
|
91
134
|
}: PopupMenuProps) => {
|
|
92
135
|
const css: any = {
|
|
93
136
|
'.popup-menu-item': {
|
|
94
137
|
padding: '0 0 1px 0',
|
|
95
138
|
display: 'inline-block',
|
|
96
139
|
position: 'relative',
|
|
97
|
-
'.
|
|
140
|
+
'.triangle-icon': {
|
|
98
141
|
display: 'inline-block',
|
|
99
142
|
cursor: 'pointer',
|
|
100
143
|
whiteSpace: 'nowrap',
|
|
101
144
|
marginRight: '15px',
|
|
102
145
|
},
|
|
103
146
|
// cover-box-shadow
|
|
104
|
-
'.
|
|
147
|
+
'.triangle-icon::after': {
|
|
105
148
|
content: '""',
|
|
106
149
|
position: 'absolute',
|
|
107
150
|
top: '50%',
|
|
@@ -125,7 +168,7 @@ export const PopupMenu = ({
|
|
|
125
168
|
position: 'absolute',
|
|
126
169
|
fontSize: 'var(--menu-font-size)',
|
|
127
170
|
top: 0,
|
|
128
|
-
|
|
171
|
+
width: '100px',
|
|
129
172
|
color: 'var(--activatable-color-normal)',
|
|
130
173
|
backgroundColor: 'var(--activatable-bg-color-normal)',
|
|
131
174
|
zIndex: 'var(--layer-menu)',
|
|
@@ -152,6 +195,15 @@ export const PopupMenu = ({
|
|
|
152
195
|
cursor: 'pointer',
|
|
153
196
|
},
|
|
154
197
|
},
|
|
198
|
+
'.popup-menu-list.left-align': {
|
|
199
|
+
left: '2px',
|
|
200
|
+
},
|
|
201
|
+
'.popup-menu-list.right-align': {
|
|
202
|
+
right: '2px',
|
|
203
|
+
},
|
|
204
|
+
'.popup-menu-list.open': {
|
|
205
|
+
display: 'inline-block',
|
|
206
|
+
},
|
|
155
207
|
},
|
|
156
208
|
};
|
|
157
209
|
|
|
@@ -163,7 +215,13 @@ export const PopupMenu = ({
|
|
|
163
215
|
handleOpened && handleOpened();
|
|
164
216
|
// console.log('=======22', event);
|
|
165
217
|
isShowing = !isShowing;
|
|
166
|
-
ref.$('.popup-menu-list')
|
|
218
|
+
const listDom = ref.$('.popup-menu-list');
|
|
219
|
+
if (align === 'left') {
|
|
220
|
+
listDom.classList.add('left-align');
|
|
221
|
+
} else {
|
|
222
|
+
listDom.classList.add('right-align');
|
|
223
|
+
}
|
|
224
|
+
listDom.classList.toggle('open', isShowing);
|
|
167
225
|
ref.$('.popup-menu-list .menu-focus').focus();
|
|
168
226
|
};
|
|
169
227
|
if (refOpenMenu) {
|
|
@@ -174,7 +232,7 @@ export const PopupMenu = ({
|
|
|
174
232
|
|
|
175
233
|
// console.log('=======', event);
|
|
176
234
|
isShowing = false;
|
|
177
|
-
ref.$('.popup-menu-list').
|
|
235
|
+
ref.$('.popup-menu-list').classList.remove('open');
|
|
178
236
|
if (event.target) {
|
|
179
237
|
if (noUpdateValue !== true) {
|
|
180
238
|
ref.$('.popup-menu-item .popup-menu-text').innerText = event.target.innerText;
|
|
@@ -187,7 +245,7 @@ export const PopupMenu = ({
|
|
|
187
245
|
};
|
|
188
246
|
const onBlur = (event: any) => {
|
|
189
247
|
setTimeout(() => {
|
|
190
|
-
ref.$('.popup-menu-list').
|
|
248
|
+
ref.$('.popup-menu-list').classList.remove('open');
|
|
191
249
|
isShowing && handleClosed && handleClosed();
|
|
192
250
|
isShowing = false;
|
|
193
251
|
}, 300);
|
|
@@ -196,7 +254,9 @@ export const PopupMenu = ({
|
|
|
196
254
|
return (
|
|
197
255
|
<div ref={ref} css={css} onClick={handleClick} title={tips}>
|
|
198
256
|
<div class='popup-menu-item'>
|
|
199
|
-
<span class='popup-menu-text'
|
|
257
|
+
<span class={'popup-menu-text' + (noTriangleIcon !== true ? ' triangle-icon' : '')}>
|
|
258
|
+
{defaultValue || ' '}
|
|
259
|
+
</span>
|
|
200
260
|
</div>
|
|
201
261
|
<div class='popup-menu-bottom'>
|
|
202
262
|
<div class='popup-menu-list'>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { VNode, CssProps, getWebVersion } from 'lupine.web';
|
|
2
|
+
import { MediaQueryMaxWidth } from '../styles';
|
|
3
|
+
import { MenuBar } from '../components';
|
|
4
|
+
|
|
5
|
+
export interface MenuBarMenuProps {
|
|
6
|
+
text: string;
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
export const DesktopFrame = async (
|
|
10
|
+
placeholderClassname: string,
|
|
11
|
+
vnode: VNode<any>,
|
|
12
|
+
title: string,
|
|
13
|
+
footerTitle: string,
|
|
14
|
+
logoUrl: string,
|
|
15
|
+
menu: MenuBarMenuProps[]
|
|
16
|
+
) => {
|
|
17
|
+
const cssContainer: CssProps = {
|
|
18
|
+
display: 'flex',
|
|
19
|
+
'flex-direction': 'column',
|
|
20
|
+
width: '100%',
|
|
21
|
+
'min-height': '100%',
|
|
22
|
+
'overflow-y': 'auto',
|
|
23
|
+
'.content-block': {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
flex: '1',
|
|
26
|
+
margin: 'auto',
|
|
27
|
+
maxWidth: MediaQueryMaxWidth.DesktopMax,
|
|
28
|
+
width: '100%',
|
|
29
|
+
},
|
|
30
|
+
'.top-footer': {
|
|
31
|
+
paddingTop: '16px',
|
|
32
|
+
},
|
|
33
|
+
'.header-box': {
|
|
34
|
+
display: 'flex',
|
|
35
|
+
'flex-direction': 'column',
|
|
36
|
+
width: '100%',
|
|
37
|
+
'min-height': '100%',
|
|
38
|
+
'overflow-y': 'auto',
|
|
39
|
+
'.content-block': {
|
|
40
|
+
display: 'flex',
|
|
41
|
+
flex: '1',
|
|
42
|
+
margin: 'auto',
|
|
43
|
+
maxWidth: MediaQueryMaxWidth.DesktopMax,
|
|
44
|
+
width: '100%',
|
|
45
|
+
},
|
|
46
|
+
'.top-footer': {
|
|
47
|
+
paddingTop: '16px',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'.footer-box': {
|
|
51
|
+
display: 'flex',
|
|
52
|
+
padding: '0 32px 16px',
|
|
53
|
+
'.footer-cp': {
|
|
54
|
+
padding: '1px 15px',
|
|
55
|
+
margin: 'auto',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div css={cssContainer}>
|
|
62
|
+
<div class='header-box'>
|
|
63
|
+
<div class='logo-box'>
|
|
64
|
+
<img class='logo' src={logoUrl} />
|
|
65
|
+
</div>
|
|
66
|
+
<div class='header-title'>
|
|
67
|
+
{title}
|
|
68
|
+
<div class='header-subtitle pt-s'>{'ver: ' + getWebVersion()}</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<MenuBar items={menu} maxWidthMobileMenu={'800px'} maxWidth={MediaQueryMaxWidth.DesktopMax}></MenuBar>
|
|
72
|
+
<div class={'content-block ' + placeholderClassname}>{vnode}</div>
|
|
73
|
+
<div class='top-footer'>
|
|
74
|
+
<div class='footer-box'>
|
|
75
|
+
<div class='footer-cp'>{footerTitle}</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { VNode, CssProps, RefProps, HtmlVar } from 'lupine.components';
|
|
2
|
+
|
|
3
|
+
export const HeaderWithBackFrameHeight = '40px';
|
|
4
|
+
export const HeaderWithBackFrameLeft = ({ onClick }: { onClick: (event: Event) => void }) => {
|
|
5
|
+
return (
|
|
6
|
+
<i class='ifc-icon mg-arrow_back_ios_new_outlined header-back-left-icon' onClick={(event) => onClick(event)}></i>
|
|
7
|
+
);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const HeaderWithBackFrameRight = ({ onClick }: { onClick: (event: Event) => void }) => {
|
|
11
|
+
return <i class='ifc-icon ma-close header-back-right-icon' onClick={(event) => onClick(event)}></i>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const HeaderWithBackFrameEmpty = () => {
|
|
15
|
+
return <div class='header-back-top-empty'></div>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export interface HeaderWithBackFrameHookProps {
|
|
19
|
+
updateTitle?: (title: string) => void;
|
|
20
|
+
updateLeft?: (left: VNode<any>) => void;
|
|
21
|
+
updateRight?: (right: VNode<any>) => void;
|
|
22
|
+
}
|
|
23
|
+
export const HeaderWithBackFrame = ({
|
|
24
|
+
children,
|
|
25
|
+
title,
|
|
26
|
+
onBack,
|
|
27
|
+
left,
|
|
28
|
+
right,
|
|
29
|
+
hook,
|
|
30
|
+
noHeader = false,
|
|
31
|
+
}: {
|
|
32
|
+
children: VNode<any>;
|
|
33
|
+
title: string;
|
|
34
|
+
onBack: (event: Event) => void;
|
|
35
|
+
left?: VNode<any>;
|
|
36
|
+
right?: VNode<any>;
|
|
37
|
+
hook?: HeaderWithBackFrameHookProps;
|
|
38
|
+
noHeader?: boolean;
|
|
39
|
+
}) => {
|
|
40
|
+
left = left || <HeaderWithBackFrameLeft onClick={onBack} />;
|
|
41
|
+
right = right || <HeaderWithBackFrameRight onClick={onBack} />;
|
|
42
|
+
const css: CssProps = {
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexDirection: 'column',
|
|
45
|
+
width: '100%',
|
|
46
|
+
height: '100%',
|
|
47
|
+
minHeight: '100%',
|
|
48
|
+
'.header-back-top': {
|
|
49
|
+
display: 'flex',
|
|
50
|
+
flexDirection: 'row',
|
|
51
|
+
width: '100vw',
|
|
52
|
+
padding: '6px 0',
|
|
53
|
+
backgroundColor: 'var(--activatable-bg-color-normal)',
|
|
54
|
+
boxShadow: '0 2px 4px var(--primary-border-color)',
|
|
55
|
+
},
|
|
56
|
+
'.header-back-content': {
|
|
57
|
+
display: 'flex',
|
|
58
|
+
flex: '1',
|
|
59
|
+
flexDirection: 'column',
|
|
60
|
+
overflowY: 'auto',
|
|
61
|
+
scrollbarWidth: 'none',
|
|
62
|
+
position: 'relative',
|
|
63
|
+
'&::-webkit-scrollbar': {
|
|
64
|
+
height: '0',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
'.header-back-title': {
|
|
68
|
+
flex: '1',
|
|
69
|
+
color: 'var(--activatable-text-color-normal)',
|
|
70
|
+
overflow: 'hidden',
|
|
71
|
+
textOverflow: 'ellipsis',
|
|
72
|
+
whiteSpace: 'nowrap',
|
|
73
|
+
},
|
|
74
|
+
'.header-back-left, .header-back-right': {
|
|
75
|
+
// width: HeaderWithBackFrameHeight,
|
|
76
|
+
height: HeaderWithBackFrameHeight,
|
|
77
|
+
display: 'flex',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
justifyContent: 'center',
|
|
80
|
+
cursor: 'pointer',
|
|
81
|
+
fontSize: '16px',
|
|
82
|
+
padding: '0 8px',
|
|
83
|
+
},
|
|
84
|
+
'.header-back-left i, .header-back-right i': {
|
|
85
|
+
fontSize: '28px',
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (hook) {
|
|
91
|
+
hook.updateTitle = (title: string) => {
|
|
92
|
+
const titleDom = ref.current?.querySelector('.header-back-title') as HTMLDivElement;
|
|
93
|
+
titleDom && (titleDom.textContent = title);
|
|
94
|
+
};
|
|
95
|
+
hook.updateLeft = (left: VNode<any>) => {
|
|
96
|
+
domLeft.value = left;
|
|
97
|
+
};
|
|
98
|
+
hook.updateRight = (right: VNode<any>) => {
|
|
99
|
+
domRight.value = right;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const domLeft = HtmlVar(left);
|
|
103
|
+
const domRight = HtmlVar(right);
|
|
104
|
+
const ref: RefProps = {};
|
|
105
|
+
return (
|
|
106
|
+
<div ref={ref} css={css} class='header-back-frame'>
|
|
107
|
+
{!noHeader && (
|
|
108
|
+
<div class='header-back-top'>
|
|
109
|
+
<div class='header-back-left'>{domLeft.node}</div>
|
|
110
|
+
<div class='mobile-title header-back-title'>{title}</div>
|
|
111
|
+
<div class='header-back-right'>{domRight.node}</div>
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
<div class='header-back-content'>{children}</div>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { VNode, CssProps, MediaQueryRange } from 'lupine.components';
|
|
2
|
+
import { MobileFooterMenu, MobileFooterMenuItemProps } from '../components/mobile-components/mobile-footer-menu';
|
|
3
|
+
import { MobileHeaderComponent } from '../components/mobile-components/mobile-header-component';
|
|
4
|
+
|
|
5
|
+
export const ResponsiveFrame = async (
|
|
6
|
+
placeholderClassname: string,
|
|
7
|
+
title: string,
|
|
8
|
+
footerTitle: string,
|
|
9
|
+
logoUrl: string,
|
|
10
|
+
vnode: VNode<any>,
|
|
11
|
+
bottomMenu: MobileFooterMenuItemProps[]
|
|
12
|
+
) => {
|
|
13
|
+
const cssContainer: CssProps = {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
width: '100%',
|
|
17
|
+
height: '100%',
|
|
18
|
+
minHeight: '100%',
|
|
19
|
+
'.frame-top-menu': {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
width: '100vw',
|
|
23
|
+
// height: '72px',
|
|
24
|
+
// position: 'fixed',
|
|
25
|
+
// left: 0,
|
|
26
|
+
// top: 0,
|
|
27
|
+
// zIndex: 'var(--layer-menu)',
|
|
28
|
+
backgroundColor: 'var(--activatable-bg-color-normal)',
|
|
29
|
+
},
|
|
30
|
+
'.frame-content': {
|
|
31
|
+
display: 'flex',
|
|
32
|
+
flex: '1',
|
|
33
|
+
flexDirection: 'column',
|
|
34
|
+
// paddingTop: '100px',
|
|
35
|
+
overflowY: 'auto',
|
|
36
|
+
scrollbarWidth: 'none',
|
|
37
|
+
'&::-webkit-scrollbar': {
|
|
38
|
+
height: '0',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
'.content-block': {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
flex: '1',
|
|
44
|
+
flexDirection: 'column',
|
|
45
|
+
overflowY: 'auto',
|
|
46
|
+
scrollbarWidth: 'none',
|
|
47
|
+
},
|
|
48
|
+
'.content-block .padding-block': {
|
|
49
|
+
padding: '0 16px',
|
|
50
|
+
},
|
|
51
|
+
// '.frame-footer': {
|
|
52
|
+
// paddingTop: '57px', // 应该和底部菜单的高度一致
|
|
53
|
+
// },
|
|
54
|
+
[MediaQueryRange.TabletBelow]: {
|
|
55
|
+
// .header-box,
|
|
56
|
+
'.frame-footer .footer-box, .frame-top-menu .desktop-menu-box': {
|
|
57
|
+
display: 'none',
|
|
58
|
+
},
|
|
59
|
+
// '.content-block': {
|
|
60
|
+
// paddingBottom: '16px',
|
|
61
|
+
// },
|
|
62
|
+
'.metronome-page-box, .gauge-box': {
|
|
63
|
+
boxShadow: '#313131 2.02px 2.02px 5.08px 1px !important',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div css={cssContainer} class='responsive-frame'>
|
|
70
|
+
<div class='frame-top-menu'>
|
|
71
|
+
{/* <DesktopTopMenu title={title}></DesktopTopMenu> */}
|
|
72
|
+
<MobileHeaderComponent></MobileHeaderComponent>
|
|
73
|
+
</div>
|
|
74
|
+
<div class='frame-content'>
|
|
75
|
+
<div class={'content-block ' + placeholderClassname}>{vnode}</div>
|
|
76
|
+
<div class='frame-footer'>
|
|
77
|
+
<div class='footer-box'>
|
|
78
|
+
<div class='footer-cp'>{footerTitle}</div>
|
|
79
|
+
</div>
|
|
80
|
+
<MobileFooterMenu items={bottomMenu}></MobileFooterMenu>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { VNode, CssProps, RefProps } from 'lupine.web';
|
|
2
|
+
import { stopPropagation } from '../lib';
|
|
3
|
+
import { HtmlVar } from '../components';
|
|
4
|
+
import { MediaQueryRange } from '../styles';
|
|
5
|
+
|
|
6
|
+
export type SliderFramePosition = 'desktop-slide-left' | 'desktop-slide-right';
|
|
7
|
+
export type SliderFrameHookProps = {
|
|
8
|
+
load?: (children: VNode<any>) => void;
|
|
9
|
+
close?: (event: Event) => void;
|
|
10
|
+
addClass?: (className: SliderFramePosition) => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SliderFrameProps = {
|
|
14
|
+
defaultContent?: VNode<any> | string;
|
|
15
|
+
direction?: 'right' | 'bottom';
|
|
16
|
+
hook?: SliderFrameHookProps;
|
|
17
|
+
afterClose?: () => void;
|
|
18
|
+
};
|
|
19
|
+
export const SliderFrame = (props: SliderFrameProps) => {
|
|
20
|
+
if (props.hook) {
|
|
21
|
+
props.hook.load = (children) => {
|
|
22
|
+
dom.value = children;
|
|
23
|
+
ref.current?.classList.remove('d-none');
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
ref.current?.classList.add('show');
|
|
26
|
+
}, 1);
|
|
27
|
+
};
|
|
28
|
+
props.hook.close = (event: Event) => {
|
|
29
|
+
stopPropagation(event);
|
|
30
|
+
ref.current?.classList.remove('show');
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
ref.current?.classList.add('d-none');
|
|
33
|
+
dom.value = '';
|
|
34
|
+
if (props.afterClose) {
|
|
35
|
+
props.afterClose();
|
|
36
|
+
}
|
|
37
|
+
}, 400);
|
|
38
|
+
};
|
|
39
|
+
props.hook.addClass = (className) => {
|
|
40
|
+
ref.current?.classList.add(className);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const dom = HtmlVar(<div class='slider-frame-default'>{props.defaultContent || '(No Content)'}</div>);
|
|
44
|
+
const ref: RefProps = {};
|
|
45
|
+
const css: CssProps = {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
flexDirection: 'column',
|
|
48
|
+
position: 'fixed',
|
|
49
|
+
top: '0',
|
|
50
|
+
left: '0',
|
|
51
|
+
right: '0',
|
|
52
|
+
bottom: '0',
|
|
53
|
+
zIndex: 'var(--layer-slider)',
|
|
54
|
+
transform: props.direction === 'bottom' ? 'translateY(100%)' : 'translateX(100%)',
|
|
55
|
+
transition: 'transform 0.4s ease-in-out',
|
|
56
|
+
backgroundColor: 'var(--primary-bg-color)',
|
|
57
|
+
'&.show': {
|
|
58
|
+
transform: props.direction === 'bottom' ? 'translateY(0)' : 'translateX(0)',
|
|
59
|
+
},
|
|
60
|
+
'& > fragment': {
|
|
61
|
+
height: '100%',
|
|
62
|
+
},
|
|
63
|
+
'&.desktop-slide-left': {
|
|
64
|
+
[MediaQueryRange.TabletAbove]: {
|
|
65
|
+
'.header-back-content': {
|
|
66
|
+
width: '30%',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
'&.desktop-slide-right': {
|
|
71
|
+
[MediaQueryRange.TabletAbove]: {
|
|
72
|
+
top: '56px',
|
|
73
|
+
left: '30%',
|
|
74
|
+
transform: 'translateX(0)',
|
|
75
|
+
'.header-back-top': {
|
|
76
|
+
width: '100%',
|
|
77
|
+
boxShadow: 'unset',
|
|
78
|
+
},
|
|
79
|
+
'.header-back-content': {
|
|
80
|
+
width: '100%',
|
|
81
|
+
},
|
|
82
|
+
'.header-back-title': {
|
|
83
|
+
fontSize: '15px',
|
|
84
|
+
},
|
|
85
|
+
'.header-back-left, .header-back-right': {
|
|
86
|
+
display: 'none',
|
|
87
|
+
},
|
|
88
|
+
'&.d-none': {
|
|
89
|
+
display: 'unset !important',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
return (
|
|
95
|
+
<div ref={ref} css={css} class='slider-frame d-none'>
|
|
96
|
+
{dom.node}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { VNode, CssProps, getWebVersion } from 'lupine.web';
|
|
2
|
+
|
|
3
|
+
export const TopFrame = async (placeholderClassname: string, vnode: VNode<any>) => {
|
|
4
|
+
const cssContainer: CssProps = {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'column',
|
|
7
|
+
width: '100%',
|
|
8
|
+
height: '100%',
|
|
9
|
+
position: 'relative',
|
|
10
|
+
'.top-frame-box': {
|
|
11
|
+
display: 'flex',
|
|
12
|
+
flex: '1',
|
|
13
|
+
flexDirection: 'column',
|
|
14
|
+
height: '100%',
|
|
15
|
+
// trick: to put two padding-top properties
|
|
16
|
+
'padding-top ': 'constant(safe-area-inset-top)',
|
|
17
|
+
'padding-top': 'env(safe-area-inset-top)',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
console.log('Web version: ', getWebVersion());
|
|
22
|
+
return (
|
|
23
|
+
<div css={cssContainer}>
|
|
24
|
+
{/* Can't put css on this placeholder node! */}
|
|
25
|
+
<div class={'top-frame-box ' + placeholderClassname}>{vnode}</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export const darkThemes: ThemeProps = {
|
|
|
9
9
|
'--scrollbar-thumb-bg': '#373636',
|
|
10
10
|
'--scrollbar-active-thumb-bg': '#5b5b5b',
|
|
11
11
|
|
|
12
|
-
'--primary-color': '#
|
|
12
|
+
'--primary-color': '#d8d8d8',
|
|
13
13
|
'--primary-color-disabled': '#7d7d7d',
|
|
14
14
|
'--primary-bg-color': '#000000',
|
|
15
15
|
'--primary-border-color': '#aeaeae',
|
|
@@ -62,7 +62,10 @@ export const darkThemes: ThemeProps = {
|
|
|
62
62
|
|
|
63
63
|
'--cover-mask-bg-color': '#878a9460',
|
|
64
64
|
'--cover-bg-color': '#202020',
|
|
65
|
+
// for dropdown-menu
|
|
65
66
|
'--cover-box-shadow': '1px 1px 4px #c6c6c6',
|
|
67
|
+
// for big block, deeper shadow arround, good for dark theme
|
|
68
|
+
'--cover-box-shadow-around': '#ffffff 0 0 6px 1px',
|
|
66
69
|
|
|
67
70
|
'--input-color': '#bdbdbd',
|
|
68
71
|
'--input-bg-color': '#000000',
|
|
@@ -79,6 +82,14 @@ export const darkThemes: ThemeProps = {
|
|
|
79
82
|
'--header-color': '#000080',
|
|
80
83
|
'--header-bg-color': '#000000',
|
|
81
84
|
|
|
85
|
+
// for setting-section
|
|
86
|
+
'--ss-group-color': 'var(--primary-color)',
|
|
87
|
+
'--ss-group-bg-color': '#232323',
|
|
88
|
+
'--ss-row-btn-color': '#eee',
|
|
89
|
+
'--ss-row-btn-bg-color': '#262626',
|
|
90
|
+
'--ss-row-btn-warn-color': 'red',
|
|
91
|
+
'--mobile-header-shadow': '0px -1px 4px 1px #848484',
|
|
92
|
+
|
|
82
93
|
// '--background-primary': '#353536', // Primary background
|
|
83
94
|
// '--color-primary': '#e0e0e0', // Primary text color
|
|
84
95
|
// backgroundPrimary: '', // Primary background
|
|
@@ -61,7 +61,10 @@ export const lightThemes: ThemeProps = {
|
|
|
61
61
|
|
|
62
62
|
'--cover-mask-bg-color': '#00000060',
|
|
63
63
|
'--cover-bg-color': '#f5f5f5',
|
|
64
|
+
// for dropdown-menu
|
|
64
65
|
'--cover-box-shadow': '3px 3px 8px #767676',
|
|
66
|
+
// for big block, deeper shadow arround, good for dark theme
|
|
67
|
+
'--cover-box-shadow-around': '#000000 2px 4px 20px 1px',
|
|
65
68
|
|
|
66
69
|
// for input, checkbox, radio box, select
|
|
67
70
|
'--input-color': '#4e4e4e',
|
|
@@ -79,6 +82,14 @@ export const lightThemes: ThemeProps = {
|
|
|
79
82
|
'--header-color': '#000080',
|
|
80
83
|
'--header-bg-color': '#ffffff',
|
|
81
84
|
|
|
85
|
+
// for setting-section
|
|
86
|
+
'--ss-group-color': 'var(--primary-color)',
|
|
87
|
+
'--ss-group-bg-color': 'var(--activatable-bg-color-selected)',
|
|
88
|
+
'--ss-row-btn-color': '#eee',
|
|
89
|
+
'--ss-row-btn-bg-color': '#333',
|
|
90
|
+
'--ss-row-btn-warn-color': 'red',
|
|
91
|
+
'--mobile-header-shadow': '0 4px 4px var(--primary-border-color)',
|
|
92
|
+
|
|
82
93
|
// '--po-background': '#e5e5e5', // Background for surfaces on top of primary background
|
|
83
94
|
// // backgroundSecondary: '#f5f5f5', // Secondary background
|
|
84
95
|
// // backgroundOnSecondary: '#e5e5e5', // Background for surfaces on top of secondary background
|