lupine.components 1.0.17 → 1.0.19

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/components/desktop-footer.tsx +17 -0
  3. package/src/components/desktop-header.tsx +52 -0
  4. package/src/components/menu-bar.tsx +1 -1
  5. package/src/components/menu-sidebar.tsx +1 -1
  6. package/src/components/mobile-components/icon-menu-item-props.ts +6 -0
  7. package/src/components/mobile-components/index.ts +7 -0
  8. package/src/components/mobile-components/mobile-footer-menu.tsx +9 -13
  9. package/src/{frames/header-with-back-frame.tsx → components/mobile-components/mobile-frame-with-header.tsx} +22 -13
  10. package/src/components/mobile-components/mobile-header-component.tsx +16 -4
  11. package/src/components/mobile-components/mobile-header-title-icon.tsx +105 -0
  12. package/src/components/mobile-components/mobile-side-menu.tsx +150 -0
  13. package/src/components/mobile-components/mobile-top-sys-icon.tsx +18 -0
  14. package/src/components/mobile-components/mobile-top-sys-menu.tsx +62 -0
  15. package/src/components/modal.tsx +2 -0
  16. package/src/components/popup-menu.tsx +30 -24
  17. package/src/components/progress.tsx +2 -2
  18. package/src/frames/index.ts +0 -2
  19. package/src/frames/responsive-frame.tsx +31 -33
  20. package/src/frames/slider-frame.tsx +20 -8
  21. package/src/frames/top-frame.tsx +4 -2
  22. package/src/lib/blob-utils.ts +23 -0
  23. package/src/lib/dom-utils.ts +32 -0
  24. package/src/lib/{dom/download.ts → download-link.ts} +1 -1
  25. package/src/lib/{dom/download-stream.ts → download-stream.ts} +3 -1
  26. package/src/lib/escape-html.ts +8 -0
  27. package/src/lib/find-parent-tag.ts +8 -0
  28. package/src/lib/index.ts +8 -1
  29. package/src/lib/path-utils.ts +37 -0
  30. package/src/frames/desktop-frame.tsx +0 -81
  31. package/src/lib/dom/index.ts +0 -71
  32. /package/src/lib/{dom/calculate-text-width.ts → calculate-text-width.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.components",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -0,0 +1,17 @@
1
+ import { CssProps } from 'lupine.components';
2
+
3
+ export const DesktopFooter = (props: { title: string }) => {
4
+ const css: CssProps = {
5
+ display: 'flex',
6
+ padding: '0 32px 16px',
7
+ '.d-footer-cp': {
8
+ padding: '1px 15px',
9
+ margin: 'auto',
10
+ },
11
+ };
12
+ return (
13
+ <div css={css} class='d-footer-box'>
14
+ <div class='d-footer-cp'>{props.title}</div>
15
+ </div>
16
+ );
17
+ };
@@ -0,0 +1,52 @@
1
+ import { CssProps } from 'lupine.components';
2
+ import { IconMenuItemProps } from './mobile-components/icon-menu-item-props';
3
+
4
+ export const DesktopHeader = (props: { title: string; items: IconMenuItemProps[] }) => {
5
+ const css: CssProps = {
6
+ display: 'flex',
7
+ flexDirection: 'row',
8
+ width: '100%',
9
+ height: '100%',
10
+ '.d-header-title': {
11
+ display: 'flex',
12
+ flex: '1',
13
+ margin: '8px 16px',
14
+ textShadow: '-3px -3px 10px white, 3px 3px 10px black',
15
+ color: 'darkblue',
16
+ fontSize: '22px',
17
+ },
18
+ '.desktop-menu-bar': {
19
+ display: 'flex',
20
+ flexDirection: 'row',
21
+ width: 'auto',
22
+ padding: '4px 16px 0',
23
+ '.desktop-menu-item': {
24
+ display: 'flex',
25
+ padding: '0 8px',
26
+ height: 'fit-content',
27
+ a: {
28
+ textDecoration: 'none',
29
+ color: 'var(--sidebar-color)',
30
+ i: {
31
+ paddingRight: '4px',
32
+ },
33
+ },
34
+ },
35
+ },
36
+ };
37
+ return (
38
+ <div css={css} class='desktop-menu-box'>
39
+ <div class='flex-1 d-header-title'>{props.title}</div>
40
+ <div class='desktop-menu-bar'>
41
+ {props.items.map((item) => (
42
+ <div class='desktop-menu-item'>
43
+ <a href={item.href}>
44
+ <i class={`ifc-icon ${item.icon}`}></i>
45
+ {item.text}
46
+ </a>
47
+ </div>
48
+ ))}
49
+ </div>
50
+ </div>
51
+ );
52
+ };
@@ -58,7 +58,7 @@ export const MenuBar = ({
58
58
  display: 'none',
59
59
  position: 'absolute',
60
60
  backgroundColor: 'var(--menubar-sub-bg-color)', //'#f9f9f9',
61
- minWidth: '160px',
61
+ minWidth: '165px',
62
62
  boxShadow: '0px 8px 16px 0px rgba(0,0,0,0.2)',
63
63
  zIndex: 'var(--layer-menu-sub)',
64
64
  flexDirection: 'column',
@@ -67,7 +67,7 @@ export const MenuSidebar = ({
67
67
  // position: 'absolute',
68
68
  // color: 'var(--sidebar-sub-color)',
69
69
  // backgroundColor: 'var(--sidebar-sub-bg-color)',
70
- minWidth: '160px',
70
+ minWidth: '165px',
71
71
  // boxShadow: '0px 8px 16px 0px rgba(0,0,0,0.2)',
72
72
  zIndex: 'var(--layer-sidebar-sub)',
73
73
  flexDirection: 'column',
@@ -0,0 +1,6 @@
1
+ export interface IconMenuItemProps {
2
+ icon: string;
3
+ href: string;
4
+ text: string;
5
+ topout?: boolean; // Topout for mobile bottom icon menu
6
+ }
@@ -1,2 +1,9 @@
1
+ export * from './icon-menu-item-props';
1
2
  export * from './mobile-footer-menu';
3
+ export * from './mobile-frame-with-header';
2
4
  export * from './mobile-header-component';
5
+ export * from './mobile-header-title-icon';
6
+ export * from './mobile-side-menu';
7
+ export * from './mobile-top-sys-icon';
8
+ export * from './mobile-top-sys-menu';
9
+
@@ -1,14 +1,8 @@
1
- import { CssProps } from 'lupine.web';
2
- import { MediaQueryRange } from '../../styles';
1
+ import { CssProps, MediaQueryRange } from 'lupine.components';
2
+ import { IconMenuItemProps } from './icon-menu-item-props';
3
3
 
4
- export interface MobileFooterMenuItemProps {
5
- icon: string;
6
- href: string;
7
- text: string;
8
- topout?: boolean;
9
- }
10
4
  export interface MobileFooterMenuProps {
11
- items: MobileFooterMenuItemProps[];
5
+ items: IconMenuItemProps[];
12
6
  color?: string;
13
7
  activeColor?: string;
14
8
  topoutColor?: string;
@@ -51,8 +45,10 @@ export const MobileFooterMenu = (props: MobileFooterMenuProps) => {
51
45
  '.footer-menu .footer-menu-item.footer-menu-topout': {
52
46
  marginTop: '-43px',
53
47
  borderRadius: '50%',
54
- backgroundColor: props.topoutBackgroundColor || '#ff8f8f',
48
+ backgroundColor: props.topoutBackgroundColor || '#f33939',
55
49
  color: props.topoutColor || 'var(--primary-color)',
50
+ },
51
+ '.footer-menu .footer-menu-item-a': {
56
52
  zIndex: 'var(--layer-header-footer)',
57
53
  },
58
54
  '.footer-menu .footer-menu-item.active': {
@@ -76,7 +72,7 @@ export const MobileFooterMenu = (props: MobileFooterMenuProps) => {
76
72
  <div css={css} class='footer-menu-box'>
77
73
  <div class='footer-menu'>
78
74
  {props.items.map((item, index) => (
79
- <a href={item.href} key={index}>
75
+ <a class='footer-menu-item-a' href={item.href} key={index}>
80
76
  <div
81
77
  class={`footer-menu-item ${item.topout ? 'footer-menu-topout' : ''} ${
82
78
  curretnUrl === item.href ? 'active' : ''
@@ -88,11 +84,11 @@ export const MobileFooterMenu = (props: MobileFooterMenuProps) => {
88
84
  </div>
89
85
  </a>
90
86
  ))}
91
- {/* <div class='footer-menu-item'><a href='/'><i class="ifc-icon bs-person-gear"></i>主页</a></div>
87
+ {/* <div class='footer-menu-item'><a href='/'><i class="ifc-icon ma-home-outline"></i>主页</a></div>
92
88
  <div class='footer-menu-item'><a href='/user/tools'><i class="ifc-icon bo-multimedia-music-note"></i>工具</a></div>
93
89
  <div class='footer-menu-item footer-menu-topout'><a href='/user/customer'><i class="ifc-icon co-cil-chat-bubble"></i>客服</a></div>
94
90
  <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> */}
91
+ <div class='footer-menu-item'><a href='/user/mine'><i class="ifc-icon ma-account-cog-outline"></i>我的</a></div> */}
96
92
  </div>
97
93
  </div>
98
94
  );
@@ -1,4 +1,9 @@
1
+ /*
2
+ HeaderWithBackFrame is a full page frame with header for mobile sliders.
3
+ It has Back icon at Left and Close icon at Right.
4
+ */
1
5
  import { VNode, CssProps, RefProps, HtmlVar } from 'lupine.components';
6
+ import { MobileHeaderTitleIcon } from './mobile-header-title-icon';
2
7
 
3
8
  export const HeaderWithBackFrameHeight = '40px';
4
9
  export const HeaderWithBackFrameLeft = ({ onClick }: { onClick: (event: Event) => void }) => {
@@ -16,10 +21,11 @@ export const HeaderWithBackFrameEmpty = () => {
16
21
  };
17
22
 
18
23
  export interface HeaderWithBackFrameHookProps {
19
- updateTitle?: (title: string) => void;
24
+ updateTitle?: (title: VNode<any> | string) => void;
20
25
  updateLeft?: (left: VNode<any>) => void;
21
26
  updateRight?: (right: VNode<any>) => void;
22
27
  }
28
+ // there may have a few HeaderWithBackFrame one over another at the same time
23
29
  export const HeaderWithBackFrame = ({
24
30
  children,
25
31
  title,
@@ -30,7 +36,7 @@ export const HeaderWithBackFrame = ({
30
36
  noHeader = false,
31
37
  }: {
32
38
  children: VNode<any>;
33
- title: string;
39
+ title: VNode<any> | string;
34
40
  onBack: (event: Event) => void;
35
41
  left?: VNode<any>;
36
42
  right?: VNode<any>;
@@ -51,7 +57,7 @@ export const HeaderWithBackFrame = ({
51
57
  width: '100vw',
52
58
  padding: '6px 0',
53
59
  backgroundColor: 'var(--activatable-bg-color-normal)',
54
- boxShadow: '0 2px 4px var(--primary-border-color)',
60
+ boxShadow: 'var(--mobile-header-shadow)',
55
61
  },
56
62
  '.header-back-content': {
57
63
  display: 'flex',
@@ -61,10 +67,12 @@ export const HeaderWithBackFrame = ({
61
67
  scrollbarWidth: 'none',
62
68
  position: 'relative',
63
69
  '&::-webkit-scrollbar': {
64
- height: '0',
70
+ display: 'none',
71
+ // height: '0',
65
72
  },
66
73
  },
67
74
  '.header-back-title': {
75
+ fontSize: '15px',
68
76
  flex: '1',
69
77
  color: 'var(--activatable-text-color-normal)',
70
78
  overflow: 'hidden',
@@ -84,13 +92,11 @@ export const HeaderWithBackFrame = ({
84
92
  '.header-back-left i, .header-back-right i': {
85
93
  fontSize: '28px',
86
94
  },
87
-
88
95
  };
89
96
 
90
97
  if (hook) {
91
- hook.updateTitle = (title: string) => {
92
- const titleDom = ref.current?.querySelector('.header-back-title') as HTMLDivElement;
93
- titleDom && (titleDom.textContent = title);
98
+ hook.updateTitle = (title: VNode<any> | string) => {
99
+ domCenter.value = title;
94
100
  };
95
101
  hook.updateLeft = (left: VNode<any>) => {
96
102
  domLeft.value = left;
@@ -100,16 +106,19 @@ export const HeaderWithBackFrame = ({
100
106
  };
101
107
  }
102
108
  const domLeft = HtmlVar(left);
109
+ const domCenter = HtmlVar(title);
103
110
  const domRight = HtmlVar(right);
104
111
  const ref: RefProps = {};
105
112
  return (
106
113
  <div ref={ref} css={css} class='header-back-frame'>
107
114
  {!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>
115
+ <MobileHeaderTitleIcon
116
+ onBack={onBack}
117
+ left={domLeft.node}
118
+ title={domCenter.node}
119
+ right={domRight.node}
120
+ hook={hook}
121
+ />
113
122
  )}
114
123
  <div class='header-back-content'>{children}</div>
115
124
  </div>
@@ -1,5 +1,16 @@
1
- import { CssProps, VNode } from 'lupine.web';
2
- import { HtmlVar } from '../html-var';
1
+ /*
2
+ MobileHeaderComponent is the topest header for mobile, and Left, Center and Right part can be updated by other components
3
+
4
+ If Left and Right are not set, then Center (100% width) can be used by temporiry headers like MobileHeaderTitleIcon,
5
+ to set Left and Right icons inside Center part.
6
+
7
+ For example, the header can be updated like this with different Left and Right icons:
8
+ <MobileHeaderCenter>
9
+ <MobileHeaderTitleIcon title='工具' left={<MobileHeaderEmptyIcon />} right={<MobileTopSysIcon />} />
10
+ </MobileHeaderCenter>
11
+ You can update Left, Right and Center separately, but it's convenient to use MobileHeaderCenter to set both Left and Right icons..
12
+ */
13
+ import { CssProps, HtmlVar, VNode } from 'lupine.components';
3
14
 
4
15
  export class MobileHeaderHelper {
5
16
  private static instance: MobileHeaderHelper;
@@ -63,14 +74,15 @@ export const MobileHeaderHide = () => {
63
74
  return <></>;
64
75
  };
65
76
 
77
+ // there should be only one MobileHeaderComponent on a page
66
78
  export const MobileHeaderComponent = (props: any) => {
67
79
  const css: CssProps = {
68
80
  display: 'flex',
69
81
  flexDirection: 'row',
70
82
  width: '100%',
71
83
  height: 'auto',
72
- padding: '2px 0',
73
- boxShadow: '0 4px 4px var(--primary-border-color)', // 第二项 2px 的话,顶部有阴影(线)
84
+ // padding: '2px 0',
85
+ // boxShadow: 'var(--mobile-header-shadow)', // 第二项 2px 的话,顶部有阴影(线)
74
86
  '& > *': {
75
87
  height: '100%',
76
88
  },
@@ -0,0 +1,105 @@
1
+ /*
2
+ MobileHeaderTitleIcon can be used in MobileHeaderComponent's Center part.
3
+ It has it's own Left and Right icons.
4
+ */
5
+ import { VNode, CssProps, HtmlVar } from 'lupine.components';
6
+
7
+ export const MobileHeadeIconHeight = '40px';
8
+ export const MobileHeadeBackIcon = ({ onClick }: { onClick: (event: Event) => void }) => {
9
+ return <i class='ifc-icon mg-arrow_back_ios_new_outlined mhti-back-icon' onClick={(event) => onClick(event)}></i>;
10
+ };
11
+
12
+ export const MobileHeadeCloseIcon = ({ onClick }: { onClick: (event: Event) => void }) => {
13
+ return <i class='ifc-icon ma-close mhti-close-icon' onClick={(event) => onClick(event)}></i>;
14
+ };
15
+
16
+ export const MobileHeaderEmptyIcon = () => {
17
+ return <div class='mhti-empty-icon' style={{ width: '28px' }}></div>;
18
+ };
19
+
20
+ export interface MobileHeaderTitleIconHookProps {
21
+ updateTitle?: (title: VNode<any> | string) => void;
22
+ updateLeft?: (left: VNode<any>) => void;
23
+ updateRight?: (right: VNode<any>) => void;
24
+ }
25
+ export interface MobileHeaderTitleIconProps {
26
+ title: VNode<any> | string;
27
+ onBack?: (event: Event) => void;
28
+ left?: VNode<any>;
29
+ right?: VNode<any>;
30
+ hook?: MobileHeaderTitleIconHookProps;
31
+ }
32
+ // there may have a few MobileHeaderTitleIcon for different pages
33
+ export const MobileHeaderTitleIcon = ({ title, onBack, left, right, hook }: MobileHeaderTitleIconProps) => {
34
+ // const processBack = (event: Event) => {
35
+ // if (onBack) {
36
+ // onBack(event);
37
+ // }
38
+ // };
39
+ // left = left || <MobileHeadeBackIcon onClick={processBack} />;
40
+ // right = right || <MobileHeadeCloseIcon onClick={processBack} />;
41
+ const css: CssProps = {
42
+ display: 'flex',
43
+ flexDirection: 'row',
44
+ width: '100vw',
45
+ padding: '6px 0',
46
+ backgroundColor: 'var(--activatable-bg-color-normal)',
47
+ boxShadow: 'var(--mobile-header-shadow)',
48
+ '.mhti-title': {
49
+ display: 'flex',
50
+ fontSize: '1.3rem',
51
+ flex: '1',
52
+ color: 'var(--activatable-text-color-normal)',
53
+ overflow: 'hidden',
54
+ textOverflow: 'ellipsis',
55
+ whiteSpace: 'nowrap',
56
+ alignItems: 'center',
57
+ justifyContent: 'center',
58
+ },
59
+ '.mhti-title > *': {
60
+ display: 'flex',
61
+ width: '100%',
62
+ alignItems: 'center',
63
+ justifyContent: 'center',
64
+ },
65
+ '.mhti-left, .mhti-right': {
66
+ height: MobileHeadeIconHeight,
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ cursor: 'pointer',
71
+ fontSize: '16px',
72
+ },
73
+ '.mhti-left': {
74
+ paddingLeft: '8px',
75
+ },
76
+ '.mhti-right': {
77
+ paddingRight: '8px',
78
+ },
79
+ '.mhti-left i, .mhti-right i': {
80
+ fontSize: '28px',
81
+ },
82
+ };
83
+
84
+ if (hook) {
85
+ hook.updateTitle = (title: VNode<any> | string) => {
86
+ domCenter.value = title;
87
+ };
88
+ hook.updateLeft = (left: VNode<any>) => {
89
+ domLeft.value = left;
90
+ };
91
+ hook.updateRight = (right: VNode<any>) => {
92
+ domRight.value = right;
93
+ };
94
+ }
95
+ const domLeft = HtmlVar(left);
96
+ const domCenter = HtmlVar(title);
97
+ const domRight = HtmlVar(right);
98
+ return (
99
+ <div css={css} class='mobile-header-title-icon-top'>
100
+ <div class='mhti-left'>{domLeft.node}</div>
101
+ <div class='mhti-title'>{domCenter.node}</div>
102
+ <div class='mhti-right'>{domRight.node}</div>
103
+ </div>
104
+ );
105
+ };
@@ -0,0 +1,150 @@
1
+ import { CssProps, RefProps, VNode } from 'lupine.components';
2
+
3
+ export class MobileSideMenuHelper {
4
+ static show() {
5
+ const ref = document.querySelector('.mobile-side-menu-mask') as HTMLDivElement;
6
+ ref.classList.add('show');
7
+ setTimeout(() => {
8
+ ref.classList.add('animate-show');
9
+ }, 1);
10
+ }
11
+
12
+ static hide() {
13
+ const ref = document.querySelector('.mobile-side-menu-mask') as HTMLDivElement;
14
+ ref.classList.remove('animate-show');
15
+ setTimeout(() => {
16
+ ref.classList.remove('show');
17
+ }, 300);
18
+ }
19
+
20
+ static isTouchEventAdded = false;
21
+ static addTouchEvent() {
22
+ if (this.isTouchEventAdded) {
23
+ return;
24
+ }
25
+
26
+ this.isTouchEventAdded = true;
27
+ let touchstartY = 0;
28
+ let touchstartX = 0;
29
+ let direction = '';
30
+ let moveStart = false;
31
+ let isOpen = false;
32
+ const maskDom = document.querySelector('.mobile-side-menu-mask') as HTMLDivElement;
33
+ document.addEventListener('touchstart', (e) => {
34
+ touchstartY = e.touches[0].clientY;
35
+ touchstartX = e.touches[0].clientX;
36
+ direction = '';
37
+ moveStart = false;
38
+ isOpen = maskDom?.classList.contains('show');
39
+ if (isOpen) {
40
+ if (touchstartX > 80) {
41
+ moveStart = true;
42
+ }
43
+ } else {
44
+ if (touchstartX < 40) {
45
+ moveStart = true;
46
+ }
47
+ }
48
+ // if (e.touches[0].clientX < 30 && !maskDom?.classList.contains('show')) {
49
+ // // 靠左边缘, 如果菜单已经打开,忽略手势
50
+ // touchStartX = e.touches[0].clientX;
51
+ // } else {
52
+ // touchStartX = -1; // 忽略非左侧边缘手势
53
+ // }
54
+ });
55
+
56
+ document.addEventListener('touchmove', (e) => {
57
+ if (!moveStart) {
58
+ return;
59
+ }
60
+
61
+ // console.log('touchmove', e.touches[0].clientX);
62
+ if (direction === '') {
63
+ if (e.touches[0].clientX - touchstartX !== 0) {
64
+ direction = 'X';
65
+ } else {
66
+ moveStart = false;
67
+ return;
68
+ }
69
+ }
70
+
71
+ if (isOpen) {
72
+ if (e.touches[0].clientX - touchstartX < 30) {
73
+ MobileSideMenuHelper.hide();
74
+ moveStart = false;
75
+ return;
76
+ }
77
+ } else {
78
+ if (e.touches[0].clientX - touchstartX > 80) {
79
+ MobileSideMenuHelper.show();
80
+ moveStart = false;
81
+ return;
82
+ }
83
+ }
84
+ });
85
+
86
+ document.addEventListener('touchend', () => {
87
+ moveStart = false;
88
+ direction = '';
89
+ });
90
+ }
91
+ }
92
+ export const MobileSideMenu = (props: { children: VNode<any> }) => {
93
+ const css: CssProps = {
94
+ '.mobile-side-menu-mask': {
95
+ display: 'none',
96
+ flexDirection: 'column',
97
+ position: 'fixed',
98
+ top: '0',
99
+ left: '0',
100
+ right: '0',
101
+ bottom: '0',
102
+ zIndex: 'var(--layer-menu)',
103
+ backgroundColor: '#000000b0',
104
+ '&.show': {
105
+ display: 'flex',
106
+ },
107
+ '&.animate-show .mobile-side-menu': {
108
+ transform: 'scaleX(1)',
109
+ },
110
+ },
111
+ '.mobile-side-menu': {
112
+ display: 'flex',
113
+ flexDirection: 'column',
114
+ padding: '16px',
115
+ transition: 'transform 0.3s ease-in-out',
116
+ backgroundColor: 'var(--primary-bg-color)',
117
+ width: '70%',
118
+ maxWidth: '300px',
119
+ height: '100%',
120
+ overflowX: 'hidden',
121
+ overflowY: 'auto',
122
+ transformOrigin: 'left',
123
+ transform: 'scaleX(0)',
124
+ // boxShadow: 'var(--block-box-shadow)',
125
+ boxShadow: 'var(--cover-box-shadow)',
126
+ },
127
+ };
128
+
129
+ const onClickContainer = (event: Event) => {
130
+ if (
131
+ event.target instanceof HTMLDivElement &&
132
+ (event.target as HTMLDivElement).classList.contains('mobile-side-menu-mask')
133
+ ) {
134
+ MobileSideMenuHelper.hide();
135
+ }
136
+ };
137
+ const ref: RefProps = {
138
+ onLoad: async () => {
139
+ MobileSideMenuHelper.addTouchEvent();
140
+ },
141
+ };
142
+ return (
143
+ <div css={css} ref={ref}>
144
+ {/* <SliderFrame hook={props.sliderFrameHook} /> */}
145
+ <div class='mobile-side-menu-mask' onClick={onClickContainer}>
146
+ <div class='mobile-side-menu'>{props.children}</div>
147
+ </div>
148
+ </div>
149
+ );
150
+ };
@@ -0,0 +1,18 @@
1
+ // import { NotificationColor, NotificationMessage, updateTheme, PopupMenuWithIcon, CssProps } from 'lupine.components';
2
+ import { CssProps } from 'lupine.components';
3
+ import { MobileSideMenuHelper } from './mobile-side-menu';
4
+
5
+ export const MobileTopSysIcon = () => {
6
+ const css: CssProps = {
7
+ cursor: 'pointer',
8
+ display: 'flex',
9
+ flexDirection: 'row',
10
+ alignItems: 'center',
11
+ fontSize: '28px',
12
+ };
13
+ return (
14
+ <div css={css} onClick={() => MobileSideMenuHelper.show()}>
15
+ <i class='ifc-icon bs-list'></i>
16
+ </div>
17
+ );
18
+ };
@@ -0,0 +1,62 @@
1
+ /*
2
+ const onLogout = async () => {
3
+ logout('/');
4
+ };
5
+ const userCookie = getCookieUser();
6
+ const listMenu = [
7
+ userCookie && (userCookie.email || userCookie.phone || userCookie.wx) ? '退出登录' : '登录',
8
+ '',
9
+ '浅色模式',
10
+ '深色模式',
11
+ '系统模式',
12
+ '',
13
+ '帮助',
14
+ '关于',
15
+ ];
16
+ if (userCookie && userCookie.admin === '1') {
17
+ listMenu.splice(0, 0, '管理员');
18
+ }
19
+
20
+ const handleSelected = (value: string) => {
21
+ if (value === '浅色模式') {
22
+ updateTheme('light');
23
+ } else if (value === '深色模式') {
24
+ updateTheme('dark');
25
+ } else if (value === '系统模式') {
26
+ const themeMedia = window.matchMedia('(prefers-color-scheme: light)');
27
+ if (themeMedia.matches) {
28
+ document.documentElement.classList.remove('dark');
29
+ document.documentElement.classList.add('light');
30
+ } else {
31
+ document.documentElement.classList.remove('light');
32
+ document.documentElement.classList.add('dark');
33
+ }
34
+ } else if (value === '退出登录') {
35
+ onLogout();
36
+ } else if (value === '登录') {
37
+ window.location.href = '/login';
38
+ } else if (value === '管理员') {
39
+ window.open('/admin');
40
+ } else {
41
+ NotificationMessage.sendMessage('Selected: ' + value, NotificationColor.Success);
42
+ }
43
+ }
44
+ */
45
+ import { PopupMenuWithIcon } from 'lupine.components';
46
+
47
+ export const MobileTopSysMenu = (props: { menuItems: string[]; handleSelected: (value: string) => void }) => {
48
+ return (
49
+ <PopupMenuWithIcon
50
+ list={props.menuItems}
51
+ defaultValue=''
52
+ tips=''
53
+ minWidth='auto'
54
+ maxWidth='200px'
55
+ maxHeight='300px'
56
+ align='right'
57
+ noTriangleIcon={true}
58
+ handleSelected={props.handleSelected}
59
+ noUpdateLabel={true}
60
+ ></PopupMenuWithIcon>
61
+ );
62
+ };
@@ -12,6 +12,7 @@ export class ModalWindow {
12
12
  closeEvent,
13
13
  handleClicked,
14
14
  closeWhenClickOutside = true,
15
+ zIndex,
15
16
  }: FloatWindowShowProps): Promise<FloatWindowCloseProps> {
16
17
  return FloatWindow.show({
17
18
  title,
@@ -24,6 +25,7 @@ export class ModalWindow {
24
25
  closeEvent,
25
26
  handleClicked,
26
27
  closeWhenClickOutside,
28
+ zIndex,
27
29
  });
28
30
  }
29
31
  }