lupine.components 1.1.13 → 1.1.15
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/README.md +3 -3
- package/package.json +42 -42
- package/src/components/action-sheet.tsx +419 -419
- package/src/components/button-push-animation.tsx +147 -138
- package/src/components/button.tsx +55 -55
- package/src/components/desktop-footer.tsx +17 -17
- package/src/components/desktop-header.tsx +52 -52
- package/src/components/drag-refresh.tsx +129 -129
- package/src/components/editable-label.tsx +83 -83
- package/src/components/float-window.tsx +233 -233
- package/src/components/grid.tsx +18 -18
- package/src/components/html-load.tsx +41 -41
- package/src/components/html-var.tsx +81 -81
- package/src/components/index.ts +43 -44
- package/src/components/input-with-title.tsx +24 -24
- package/src/components/link-item.tsx +13 -13
- package/src/components/link-list.tsx +62 -62
- package/src/components/menu-bar.tsx +219 -219
- package/src/components/menu-item-props.tsx +13 -13
- package/src/components/menu-sidebar.tsx +325 -318
- package/src/components/message-box.tsx +44 -44
- package/src/components/meta-data.tsx +36 -36
- package/src/components/meta-description.tsx +12 -12
- package/src/components/mobile-components/icon-menu-item-props.ts +6 -6
- package/src/components/mobile-components/index.ts +8 -9
- package/src/components/mobile-components/mobile-footer-menu.tsx +95 -95
- package/src/components/mobile-components/mobile-header-component.tsx +101 -101
- package/src/components/mobile-components/mobile-header-title-icon.tsx +109 -101
- package/src/components/mobile-components/mobile-header-with-back.tsx +127 -117
- package/src/components/mobile-components/mobile-side-menu.tsx +154 -154
- package/src/components/mobile-components/mobile-top-sys-icon.tsx +18 -18
- package/src/components/mobile-components/mobile-top-sys-menu.tsx +62 -62
- package/src/components/modal.tsx +33 -33
- package/src/components/notice-message.tsx +118 -118
- package/src/components/page-title.tsx +6 -6
- package/src/components/paging-link.tsx +175 -175
- package/src/components/panel.tsx +21 -21
- package/src/components/popup-menu.tsx +289 -289
- package/src/components/progress.tsx +91 -91
- package/src/components/radio-label-component.tsx +36 -36
- package/src/components/redirect.tsx +19 -19
- package/src/components/resizable-splitter.tsx +128 -128
- package/src/components/select-angle-component.tsx +127 -127
- package/src/components/select-with-title.tsx +37 -37
- package/src/components/slide-tab-component.tsx +144 -149
- package/src/components/spinner.tsx +106 -100
- package/src/components/stars-component.tsx +66 -66
- package/src/components/svg.tsx +24 -24
- package/src/components/tabs.tsx +279 -279
- package/src/components/text-glow.tsx +37 -37
- package/src/components/text-scale.tsx +42 -42
- package/src/components/text-wave.tsx +55 -55
- package/src/components/theme-selector.tsx +28 -28
- package/src/components/toggle-base.tsx +285 -269
- package/src/components/toggle-switch.tsx +160 -160
- package/src/frames/index.ts +3 -3
- package/src/frames/responsive-frame.tsx +83 -83
- package/src/frames/slider-frame.tsx +111 -111
- package/src/frames/top-frame.tsx +30 -30
- package/src/index.ts +5 -5
- package/src/lib/back-action-helper.ts +54 -54
- package/src/lib/base62.ts +23 -23
- package/src/lib/blob-utils.ts +23 -23
- package/src/lib/calculate-text-width.ts +13 -13
- package/src/lib/date-utils.ts +317 -317
- package/src/lib/deep-merge.ts +37 -37
- package/src/lib/document-ready.ts +34 -34
- package/src/lib/dom-utils.ts +32 -32
- package/src/lib/download-file.ts +118 -118
- package/src/lib/download-link.ts +12 -12
- package/src/lib/download-stream.ts +19 -19
- package/src/lib/drag-util.ts +118 -118
- package/src/lib/dynamical-load.ts +134 -134
- package/src/lib/encode-html.ts +27 -27
- package/src/lib/find-parent-tag.ts +8 -8
- package/src/lib/format-bytes.ts +11 -11
- package/src/lib/index.ts +24 -24
- package/src/lib/lite-dom.ts +225 -225
- package/src/lib/message-hub.ts +103 -104
- package/src/lib/observable.ts +188 -188
- package/src/lib/path-utils.ts +42 -42
- package/src/lib/promise-timeout.ts +1 -1
- package/src/lib/simple-storage.ts +40 -40
- package/src/lib/stop-propagation.ts +7 -7
- package/src/lib/upload-file.ts +101 -101
- package/src/styles/base-themes.ts +17 -17
- package/src/styles/dark-themes.ts +99 -99
- package/src/styles/index.ts +5 -5
- package/src/styles/light-themes.ts +106 -106
- package/src/styles/media-query.ts +93 -93
- package/src/styles/shared-themes.ts +57 -57
- package/tsconfig.json +113 -113
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import { CssProps, RefProps } from 'lupine.web';
|
|
2
|
-
import { HtmlVar } from './html-var';
|
|
3
|
-
|
|
4
|
-
export type ProgressHookProps = {
|
|
5
|
-
onProgress?: (percentage: number, chunkNumber: number, totalChunks: number) => void;
|
|
6
|
-
onShow?: (show: boolean, title?: string) => void;
|
|
7
|
-
};
|
|
8
|
-
export type ProgressProps = {
|
|
9
|
-
hook: ProgressHookProps;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const Progress = (props: ProgressProps) => {
|
|
13
|
-
const css: CssProps = {
|
|
14
|
-
position: 'fixed',
|
|
15
|
-
display: 'flex',
|
|
16
|
-
bottom: '0',
|
|
17
|
-
left: '0',
|
|
18
|
-
width: '100%',
|
|
19
|
-
zIndex: 'var(--layer-modal-over)',
|
|
20
|
-
flexDirection: 'column',
|
|
21
|
-
backgroundColor: '#e6e6e6de',
|
|
22
|
-
padding: '16px',
|
|
23
|
-
'.progress-box': {
|
|
24
|
-
display: 'flex',
|
|
25
|
-
flexDirection: 'column',
|
|
26
|
-
alignItems: 'center',
|
|
27
|
-
justifyContent: 'center',
|
|
28
|
-
width: '100%',
|
|
29
|
-
height: 'auto',
|
|
30
|
-
margin: 'auto',
|
|
31
|
-
},
|
|
32
|
-
'.progress-bar': {
|
|
33
|
-
display: 'flex',
|
|
34
|
-
width: '100%',
|
|
35
|
-
height: '60px',
|
|
36
|
-
borderRadius: '4px',
|
|
37
|
-
overflow: 'hidden',
|
|
38
|
-
},
|
|
39
|
-
'.progress-bar1': {
|
|
40
|
-
height: '100%',
|
|
41
|
-
width: '0%',
|
|
42
|
-
backgroundColor: '#49e57e',
|
|
43
|
-
},
|
|
44
|
-
'.progress-bar2': {
|
|
45
|
-
height: '100%',
|
|
46
|
-
width: '100%',
|
|
47
|
-
backgroundColor: '#2bb8cd',
|
|
48
|
-
},
|
|
49
|
-
'.progress-tips': {
|
|
50
|
-
marginTop: '10px',
|
|
51
|
-
fontSize: '30px',
|
|
52
|
-
color: '#49e57e',
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
props.hook.onProgress = (percentage: number, chunkNumber: number, totalChunks: number) => {
|
|
57
|
-
// console.log(`Upload progress: ${percentage}% (chunk ${chunkNumber} of ${totalChunks})`);
|
|
58
|
-
percentage = Math.round(percentage * 100);
|
|
59
|
-
const bar1 = document.querySelector('.progress-bar1') as HTMLElement;
|
|
60
|
-
const bar2 = document.querySelector('.progress-bar2') as HTMLElement;
|
|
61
|
-
bar1.style.width = `${percentage}%`;
|
|
62
|
-
bar2.style.width = `${1 - percentage}%`;
|
|
63
|
-
dom.value = `${percentage}%`;
|
|
64
|
-
};
|
|
65
|
-
props.hook.onShow = (show: boolean, title?: string) => {
|
|
66
|
-
if (title) {
|
|
67
|
-
domTitle.value = title;
|
|
68
|
-
}
|
|
69
|
-
if (show) {
|
|
70
|
-
ref.current?.classList.remove('d-none');
|
|
71
|
-
} else {
|
|
72
|
-
ref.current?.classList.add('d-none');
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const ref: RefProps = {};
|
|
77
|
-
const domTitle = new HtmlVar('Progress');
|
|
78
|
-
const dom = new HtmlVar('0 %');
|
|
79
|
-
return (
|
|
80
|
-
<div ref={ref} css={css} class='progress-top d-none'>
|
|
81
|
-
<div class='progress-box'>
|
|
82
|
-
<div class='progress-title mb-m align-left w-100p'>{domTitle.node}</div>
|
|
83
|
-
<div class='progress-bar'>
|
|
84
|
-
<div class='progress-bar1'></div>
|
|
85
|
-
<div class='progress-bar2'></div>
|
|
86
|
-
</div>
|
|
87
|
-
<div class='progress-tips'>{dom.node}</div>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
};
|
|
1
|
+
import { CssProps, RefProps } from 'lupine.web';
|
|
2
|
+
import { HtmlVar } from './html-var';
|
|
3
|
+
|
|
4
|
+
export type ProgressHookProps = {
|
|
5
|
+
onProgress?: (percentage: number, chunkNumber: number, totalChunks: number) => void;
|
|
6
|
+
onShow?: (show: boolean, title?: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export type ProgressProps = {
|
|
9
|
+
hook: ProgressHookProps;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const Progress = (props: ProgressProps) => {
|
|
13
|
+
const css: CssProps = {
|
|
14
|
+
position: 'fixed',
|
|
15
|
+
display: 'flex',
|
|
16
|
+
bottom: '0',
|
|
17
|
+
left: '0',
|
|
18
|
+
width: '100%',
|
|
19
|
+
zIndex: 'var(--layer-modal-over)',
|
|
20
|
+
flexDirection: 'column',
|
|
21
|
+
backgroundColor: '#e6e6e6de',
|
|
22
|
+
padding: '16px',
|
|
23
|
+
'.progress-box': {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
flexDirection: 'column',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
justifyContent: 'center',
|
|
28
|
+
width: '100%',
|
|
29
|
+
height: 'auto',
|
|
30
|
+
margin: 'auto',
|
|
31
|
+
},
|
|
32
|
+
'.progress-bar': {
|
|
33
|
+
display: 'flex',
|
|
34
|
+
width: '100%',
|
|
35
|
+
height: '60px',
|
|
36
|
+
borderRadius: '4px',
|
|
37
|
+
overflow: 'hidden',
|
|
38
|
+
},
|
|
39
|
+
'.progress-bar1': {
|
|
40
|
+
height: '100%',
|
|
41
|
+
width: '0%',
|
|
42
|
+
backgroundColor: '#49e57e',
|
|
43
|
+
},
|
|
44
|
+
'.progress-bar2': {
|
|
45
|
+
height: '100%',
|
|
46
|
+
width: '100%',
|
|
47
|
+
backgroundColor: '#2bb8cd',
|
|
48
|
+
},
|
|
49
|
+
'.progress-tips': {
|
|
50
|
+
marginTop: '10px',
|
|
51
|
+
fontSize: '30px',
|
|
52
|
+
color: '#49e57e',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
props.hook.onProgress = (percentage: number, chunkNumber: number, totalChunks: number) => {
|
|
57
|
+
// console.log(`Upload progress: ${percentage}% (chunk ${chunkNumber} of ${totalChunks})`);
|
|
58
|
+
percentage = Math.round(percentage * 100);
|
|
59
|
+
const bar1 = document.querySelector('.progress-bar1') as HTMLElement;
|
|
60
|
+
const bar2 = document.querySelector('.progress-bar2') as HTMLElement;
|
|
61
|
+
bar1.style.width = `${percentage}%`;
|
|
62
|
+
bar2.style.width = `${1 - percentage}%`;
|
|
63
|
+
dom.value = `${percentage}%`;
|
|
64
|
+
};
|
|
65
|
+
props.hook.onShow = (show: boolean, title?: string) => {
|
|
66
|
+
if (title) {
|
|
67
|
+
domTitle.value = title;
|
|
68
|
+
}
|
|
69
|
+
if (show) {
|
|
70
|
+
ref.current?.classList.remove('d-none');
|
|
71
|
+
} else {
|
|
72
|
+
ref.current?.classList.add('d-none');
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const ref: RefProps = {};
|
|
77
|
+
const domTitle = new HtmlVar('Progress');
|
|
78
|
+
const dom = new HtmlVar('0 %');
|
|
79
|
+
return (
|
|
80
|
+
<div ref={ref} css={css} class='progress-top d-none'>
|
|
81
|
+
<div class='progress-box'>
|
|
82
|
+
<div class='progress-title mb-m align-left w-100p'>{domTitle.node}</div>
|
|
83
|
+
<div class='progress-bar'>
|
|
84
|
+
<div class='progress-bar1'></div>
|
|
85
|
+
<div class='progress-bar2'></div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class='progress-tips'>{dom.node}</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { bindGlobalStyle, CssProps } from 'lupine.components';
|
|
2
|
-
|
|
3
|
-
export const RadioLabelComponent = (props: {
|
|
4
|
-
label: string;
|
|
5
|
-
name: string;
|
|
6
|
-
checked?: boolean;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
onChange?: (checked: boolean) => void;
|
|
9
|
-
className?: string;
|
|
10
|
-
radioClassname?: string;
|
|
11
|
-
}) => {
|
|
12
|
-
const css: CssProps = {
|
|
13
|
-
display: 'flex',
|
|
14
|
-
'& > label': {
|
|
15
|
-
display: 'flex',
|
|
16
|
-
alignItems: 'center',
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
bindGlobalStyle('radio-label-component', css);
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div class={'radio-label-component' + (props.className ? ' ' + props.className : '')}>
|
|
23
|
-
<label>
|
|
24
|
-
<input
|
|
25
|
-
type='radio'
|
|
26
|
-
name={props.name}
|
|
27
|
-
class={'input-base input-s' + (props.radioClassname ? ' ' + props.radioClassname : '')}
|
|
28
|
-
checked={props.checked}
|
|
29
|
-
disabled={props.disabled}
|
|
30
|
-
onChange={(event) => props.onChange?.((event.target as HTMLInputElement).checked)}
|
|
31
|
-
/>
|
|
32
|
-
<span class='ml-ss'>{props.label}</span>
|
|
33
|
-
</label>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
1
|
+
import { bindGlobalStyle, CssProps } from 'lupine.components';
|
|
2
|
+
|
|
3
|
+
export const RadioLabelComponent = (props: {
|
|
4
|
+
label: string;
|
|
5
|
+
name: string;
|
|
6
|
+
checked?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
onChange?: (checked: boolean) => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
radioClassname?: string;
|
|
11
|
+
}) => {
|
|
12
|
+
const css: CssProps = {
|
|
13
|
+
display: 'flex',
|
|
14
|
+
'& > label': {
|
|
15
|
+
display: 'flex',
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
bindGlobalStyle('radio-label-component', css);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div class={'radio-label-component' + (props.className ? ' ' + props.className : '')}>
|
|
23
|
+
<label>
|
|
24
|
+
<input
|
|
25
|
+
type='radio'
|
|
26
|
+
name={props.name}
|
|
27
|
+
class={'input-base input-s' + (props.radioClassname ? ' ' + props.radioClassname : '')}
|
|
28
|
+
checked={props.checked}
|
|
29
|
+
disabled={props.disabled}
|
|
30
|
+
onChange={(event) => props.onChange?.((event.target as HTMLInputElement).checked)}
|
|
31
|
+
/>
|
|
32
|
+
<span class='ml-ss'>{props.label}</span>
|
|
33
|
+
</label>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { RefProps } from 'lupine.web';
|
|
2
|
-
|
|
3
|
-
export type RedirectProps = {
|
|
4
|
-
title?: string;
|
|
5
|
-
url: string;
|
|
6
|
-
delaySeconds?: number;
|
|
7
|
-
};
|
|
8
|
-
export const Redirect = ({ title = 'redirect...', url, delaySeconds = 0 }: RedirectProps) => {
|
|
9
|
-
// if SSR is disabled, then MetaData will not be working
|
|
10
|
-
// MetaData({ httpEquiv: 'refresh', content: delaySeconds + ';URL=' + url });
|
|
11
|
-
const ref: RefProps = {
|
|
12
|
-
onLoad: async (el: Element) => {
|
|
13
|
-
setTimeout(() => {
|
|
14
|
-
window.location.href = url;
|
|
15
|
-
}, delaySeconds * 1000);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
return <div ref={ref}>{title}</div>;
|
|
19
|
-
};
|
|
1
|
+
import { RefProps } from 'lupine.web';
|
|
2
|
+
|
|
3
|
+
export type RedirectProps = {
|
|
4
|
+
title?: string;
|
|
5
|
+
url: string;
|
|
6
|
+
delaySeconds?: number;
|
|
7
|
+
};
|
|
8
|
+
export const Redirect = ({ title = 'redirect...', url, delaySeconds = 0 }: RedirectProps) => {
|
|
9
|
+
// if SSR is disabled, then MetaData will not be working
|
|
10
|
+
// MetaData({ httpEquiv: 'refresh', content: delaySeconds + ';URL=' + url });
|
|
11
|
+
const ref: RefProps = {
|
|
12
|
+
onLoad: async (el: Element) => {
|
|
13
|
+
setTimeout(() => {
|
|
14
|
+
window.location.href = url;
|
|
15
|
+
}, delaySeconds * 1000);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return <div ref={ref}>{title}</div>;
|
|
19
|
+
};
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
import { bindGlobalStyle, CssProps } from 'lupine.web';
|
|
2
|
-
import { stopPropagation } from '../lib';
|
|
3
|
-
/**
|
|
4
|
-
How to use:
|
|
5
|
-
// getSplitter's first parameter is the container's selector where the Splitter will be in.
|
|
6
|
-
// the second parameter is whether it's Vertical
|
|
7
|
-
// then the third parameter is it's RightOrTop or not
|
|
8
|
-
const locationLeft = false;
|
|
9
|
-
const splitter = ResizableSplitter.getSplitter('.body .side', true, locationLeft);
|
|
10
|
-
const container = (
|
|
11
|
-
<div class='body'>
|
|
12
|
-
<div class='side'>
|
|
13
|
-
{splitter}
|
|
14
|
-
{designPanel}
|
|
15
|
-
</div>
|
|
16
|
-
...
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
export class ResizableSplitter {
|
|
20
|
-
static hostNode: HTMLElement;
|
|
21
|
-
static isVertical = true;
|
|
22
|
-
static isRightOrTop = true;
|
|
23
|
-
|
|
24
|
-
private static initialized = false;
|
|
25
|
-
private static startXorY = 0;
|
|
26
|
-
private static startWidthOrHeight = 0;
|
|
27
|
-
private static pressed = false;
|
|
28
|
-
|
|
29
|
-
static init() {
|
|
30
|
-
/* styles for resizable splitter */
|
|
31
|
-
const css: CssProps = {
|
|
32
|
-
'.resizable-splitter-v-left, .resizable-splitter-v-right': {
|
|
33
|
-
position: 'absolute',
|
|
34
|
-
top: 0,
|
|
35
|
-
bottom: 0,
|
|
36
|
-
left: 0,
|
|
37
|
-
width: '2px',
|
|
38
|
-
cursor: 'col-resize',
|
|
39
|
-
},
|
|
40
|
-
'.resizable-splitter-v-right': {
|
|
41
|
-
left: 'unset',
|
|
42
|
-
right: 0,
|
|
43
|
-
},
|
|
44
|
-
'.resizable-splitter-v-left:hover, .resizable-splitter-v-right:hover': {
|
|
45
|
-
width: '4px',
|
|
46
|
-
backgroundColor: '#ccc',
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
'.resizable-splitter-h-top, .resizable-splitter-h-bottom': {
|
|
50
|
-
position: 'absolute',
|
|
51
|
-
top: 0,
|
|
52
|
-
left: 0,
|
|
53
|
-
right: 0,
|
|
54
|
-
height: '2px',
|
|
55
|
-
cursor: 'row-resize',
|
|
56
|
-
},
|
|
57
|
-
'.resizable-splitter-h-bottom': {
|
|
58
|
-
top: 'unset',
|
|
59
|
-
bottom: 0,
|
|
60
|
-
},
|
|
61
|
-
'.resizable-splitter-h-top:hover, .resizable-splitter-h-bottom:hover': {
|
|
62
|
-
height: '4px',
|
|
63
|
-
backgroundColor: '#ccc',
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
bindGlobalStyle('resizable-splitter', css);
|
|
67
|
-
|
|
68
|
-
window.addEventListener('mousemove', ResizableSplitter.onMousemove.bind(ResizableSplitter), false);
|
|
69
|
-
document.documentElement.addEventListener('mouseup', ResizableSplitter.onMouseup.bind(ResizableSplitter), false);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
static getSplitterClassName(isVertical: boolean, isRightOrTop: boolean): string {
|
|
73
|
-
const className =
|
|
74
|
-
'resizable-splitter-' +
|
|
75
|
-
(isVertical ? (isRightOrTop ? 'v-right' : 'v-left') : isRightOrTop ? 'h-top' : 'h-bottom');
|
|
76
|
-
return className;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static onMousedown(event: any) {
|
|
80
|
-
if (event.buttons !== 1 || event.button !== 0) return;
|
|
81
|
-
this.pressed = true;
|
|
82
|
-
this.startXorY = this.isVertical ? event.clientX : event.clientY;
|
|
83
|
-
const startPosition = document.defaultView!.getComputedStyle(this.hostNode)[this.isVertical ? 'width' : 'height'];
|
|
84
|
-
this.startWidthOrHeight = parseInt(startPosition, 10);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
static onMousemove(event: any) {
|
|
88
|
-
if (!this.pressed || event.buttons !== 1 || event.button !== 0) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// prevent text/element selection when drag
|
|
93
|
-
stopPropagation(event);
|
|
94
|
-
if (this.isVertical) {
|
|
95
|
-
const movedXorY = this.startWidthOrHeight + (event.clientX - this.startXorY) * (this.isRightOrTop ? 1 : -1);
|
|
96
|
-
this.hostNode.style.width = movedXorY + 'px';
|
|
97
|
-
} else {
|
|
98
|
-
const movedXorY = this.startWidthOrHeight + (event.clientY - this.startXorY) * (this.isRightOrTop ? -1 : 1);
|
|
99
|
-
this.hostNode.style.height = movedXorY + 'px';
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static onMouseup() {
|
|
104
|
-
if (this.pressed) this.pressed = false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
static getSplitter(selector: string, isVertical: boolean, isRightOrTop: boolean) {
|
|
108
|
-
const className = this.getSplitterClassName(isVertical, isRightOrTop);
|
|
109
|
-
|
|
110
|
-
const onMousedown = (event: any) => {
|
|
111
|
-
if (!this.initialized) {
|
|
112
|
-
this.initialized = true;
|
|
113
|
-
this.init();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
ResizableSplitter.hostNode = document.querySelector(selector)!;
|
|
117
|
-
if (!ResizableSplitter.hostNode) {
|
|
118
|
-
console.error(`Can't find element: ${selector}`);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
ResizableSplitter.isVertical = isVertical;
|
|
122
|
-
ResizableSplitter.isRightOrTop = isRightOrTop;
|
|
123
|
-
ResizableSplitter.onMousedown.bind(ResizableSplitter)(event);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
return <div onMouseDown={onMousedown} class={className}></div>;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
1
|
+
import { bindGlobalStyle, CssProps } from 'lupine.web';
|
|
2
|
+
import { stopPropagation } from '../lib';
|
|
3
|
+
/**
|
|
4
|
+
How to use:
|
|
5
|
+
// getSplitter's first parameter is the container's selector where the Splitter will be in.
|
|
6
|
+
// the second parameter is whether it's Vertical
|
|
7
|
+
// then the third parameter is it's RightOrTop or not
|
|
8
|
+
const locationLeft = false;
|
|
9
|
+
const splitter = ResizableSplitter.getSplitter('.body .side', true, locationLeft);
|
|
10
|
+
const container = (
|
|
11
|
+
<div class='body'>
|
|
12
|
+
<div class='side'>
|
|
13
|
+
{splitter}
|
|
14
|
+
{designPanel}
|
|
15
|
+
</div>
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
export class ResizableSplitter {
|
|
20
|
+
static hostNode: HTMLElement;
|
|
21
|
+
static isVertical = true;
|
|
22
|
+
static isRightOrTop = true;
|
|
23
|
+
|
|
24
|
+
private static initialized = false;
|
|
25
|
+
private static startXorY = 0;
|
|
26
|
+
private static startWidthOrHeight = 0;
|
|
27
|
+
private static pressed = false;
|
|
28
|
+
|
|
29
|
+
static init() {
|
|
30
|
+
/* styles for resizable splitter */
|
|
31
|
+
const css: CssProps = {
|
|
32
|
+
'.resizable-splitter-v-left, .resizable-splitter-v-right': {
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
top: 0,
|
|
35
|
+
bottom: 0,
|
|
36
|
+
left: 0,
|
|
37
|
+
width: '2px',
|
|
38
|
+
cursor: 'col-resize',
|
|
39
|
+
},
|
|
40
|
+
'.resizable-splitter-v-right': {
|
|
41
|
+
left: 'unset',
|
|
42
|
+
right: 0,
|
|
43
|
+
},
|
|
44
|
+
'.resizable-splitter-v-left:hover, .resizable-splitter-v-right:hover': {
|
|
45
|
+
width: '4px',
|
|
46
|
+
backgroundColor: '#ccc',
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
'.resizable-splitter-h-top, .resizable-splitter-h-bottom': {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
top: 0,
|
|
52
|
+
left: 0,
|
|
53
|
+
right: 0,
|
|
54
|
+
height: '2px',
|
|
55
|
+
cursor: 'row-resize',
|
|
56
|
+
},
|
|
57
|
+
'.resizable-splitter-h-bottom': {
|
|
58
|
+
top: 'unset',
|
|
59
|
+
bottom: 0,
|
|
60
|
+
},
|
|
61
|
+
'.resizable-splitter-h-top:hover, .resizable-splitter-h-bottom:hover': {
|
|
62
|
+
height: '4px',
|
|
63
|
+
backgroundColor: '#ccc',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
bindGlobalStyle('resizable-splitter', css);
|
|
67
|
+
|
|
68
|
+
window.addEventListener('mousemove', ResizableSplitter.onMousemove.bind(ResizableSplitter), false);
|
|
69
|
+
document.documentElement.addEventListener('mouseup', ResizableSplitter.onMouseup.bind(ResizableSplitter), false);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static getSplitterClassName(isVertical: boolean, isRightOrTop: boolean): string {
|
|
73
|
+
const className =
|
|
74
|
+
'resizable-splitter-' +
|
|
75
|
+
(isVertical ? (isRightOrTop ? 'v-right' : 'v-left') : isRightOrTop ? 'h-top' : 'h-bottom');
|
|
76
|
+
return className;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static onMousedown(event: any) {
|
|
80
|
+
if (event.buttons !== 1 || event.button !== 0) return;
|
|
81
|
+
this.pressed = true;
|
|
82
|
+
this.startXorY = this.isVertical ? event.clientX : event.clientY;
|
|
83
|
+
const startPosition = document.defaultView!.getComputedStyle(this.hostNode)[this.isVertical ? 'width' : 'height'];
|
|
84
|
+
this.startWidthOrHeight = parseInt(startPosition, 10);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static onMousemove(event: any) {
|
|
88
|
+
if (!this.pressed || event.buttons !== 1 || event.button !== 0) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// prevent text/element selection when drag
|
|
93
|
+
stopPropagation(event);
|
|
94
|
+
if (this.isVertical) {
|
|
95
|
+
const movedXorY = this.startWidthOrHeight + (event.clientX - this.startXorY) * (this.isRightOrTop ? 1 : -1);
|
|
96
|
+
this.hostNode.style.width = movedXorY + 'px';
|
|
97
|
+
} else {
|
|
98
|
+
const movedXorY = this.startWidthOrHeight + (event.clientY - this.startXorY) * (this.isRightOrTop ? -1 : 1);
|
|
99
|
+
this.hostNode.style.height = movedXorY + 'px';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static onMouseup() {
|
|
104
|
+
if (this.pressed) this.pressed = false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static getSplitter(selector: string, isVertical: boolean, isRightOrTop: boolean) {
|
|
108
|
+
const className = this.getSplitterClassName(isVertical, isRightOrTop);
|
|
109
|
+
|
|
110
|
+
const onMousedown = (event: any) => {
|
|
111
|
+
if (!this.initialized) {
|
|
112
|
+
this.initialized = true;
|
|
113
|
+
this.init();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
ResizableSplitter.hostNode = document.querySelector(selector)!;
|
|
117
|
+
if (!ResizableSplitter.hostNode) {
|
|
118
|
+
console.error(`Can't find element: ${selector}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
ResizableSplitter.isVertical = isVertical;
|
|
122
|
+
ResizableSplitter.isRightOrTop = isRightOrTop;
|
|
123
|
+
ResizableSplitter.onMousedown.bind(ResizableSplitter)(event);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return <div onMouseDown={onMousedown} class={className}></div>;
|
|
127
|
+
}
|
|
128
|
+
}
|