goblin-magic 1.0.3
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/.editorconfig +9 -0
- package/.zou-flow +2 -0
- package/eslint.config.js +65 -0
- package/magicNavigation.js +7 -0
- package/package.json +45 -0
- package/widgets/dialog/widget.js +78 -0
- package/widgets/element-helpers/element-has-direct-text.js +9 -0
- package/widgets/element-helpers/is-empty-area-element.js +17 -0
- package/widgets/element-helpers/is-flat-element.js +52 -0
- package/widgets/get-modifiers/get-modifiers.js +34 -0
- package/widgets/input-group/styles.js +74 -0
- package/widgets/input-group/widget.js +24 -0
- package/widgets/magic-action/styles.js +45 -0
- package/widgets/magic-action/widget.js +44 -0
- package/widgets/magic-background/bg-alps.jpg +0 -0
- package/widgets/magic-background/bg-fur.png +0 -0
- package/widgets/magic-background/bg-milkyway.png +0 -0
- package/widgets/magic-background/bg-space.jpg +0 -0
- package/widgets/magic-background/bg-synth.jpg +0 -0
- package/widgets/magic-background/bg-white.png +0 -0
- package/widgets/magic-background/styles.js +81 -0
- package/widgets/magic-background/widget.js +20 -0
- package/widgets/magic-box/styles.js +10 -0
- package/widgets/magic-box/widget.js +28 -0
- package/widgets/magic-box-old/styles.js +111 -0
- package/widgets/magic-box-old/widget.js +30 -0
- package/widgets/magic-button/styles.js +156 -0
- package/widgets/magic-button/widget.js +89 -0
- package/widgets/magic-checkbox/styles.js +116 -0
- package/widgets/magic-checkbox/widget.js +68 -0
- package/widgets/magic-color-field/styles.js +22 -0
- package/widgets/magic-color-field/widget.js +68 -0
- package/widgets/magic-date-field/styles.js +9 -0
- package/widgets/magic-date-field/widget.js +145 -0
- package/widgets/magic-datetime-field/styles.js +11 -0
- package/widgets/magic-datetime-field/widget.js +95 -0
- package/widgets/magic-dialog/styles.js +39 -0
- package/widgets/magic-dialog/widget.js +116 -0
- package/widgets/magic-div/styles.js +22 -0
- package/widgets/magic-div/widget.js +20 -0
- package/widgets/magic-emoji/styles.js +14 -0
- package/widgets/magic-emoji/widget.js +33 -0
- package/widgets/magic-emoji-picker/styles.js +21 -0
- package/widgets/magic-emoji-picker/widget.js +44 -0
- package/widgets/magic-inplace-input/styles.js +55 -0
- package/widgets/magic-inplace-input/widget.js +26 -0
- package/widgets/magic-input/styles.js +50 -0
- package/widgets/magic-input/widget.js +397 -0
- package/widgets/magic-label/styles.js +20 -0
- package/widgets/magic-label/widget.js +24 -0
- package/widgets/magic-navigation/service.js +1306 -0
- package/widgets/magic-navigation/styles.js +103 -0
- package/widgets/magic-navigation/view-context.js +15 -0
- package/widgets/magic-navigation/widget.js +540 -0
- package/widgets/magic-number-field/styles.js +10 -0
- package/widgets/magic-number-field/widget.js +103 -0
- package/widgets/magic-panel/styles.js +61 -0
- package/widgets/magic-panel/widget.js +63 -0
- package/widgets/magic-radio/styles.js +93 -0
- package/widgets/magic-radio/widget.js +74 -0
- package/widgets/magic-scroll/styles.js +22 -0
- package/widgets/magic-scroll/widget.js +20 -0
- package/widgets/magic-select/styles.js +16 -0
- package/widgets/magic-select/widget.js +134 -0
- package/widgets/magic-table/reducer.js +63 -0
- package/widgets/magic-table/styles.js +170 -0
- package/widgets/magic-table/widget.js +627 -0
- package/widgets/magic-tag/styles.js +32 -0
- package/widgets/magic-tag/widget.js +32 -0
- package/widgets/magic-text-field/styles.js +58 -0
- package/widgets/magic-text-field/widget.js +66 -0
- package/widgets/magic-time-field/styles.js +8 -0
- package/widgets/magic-time-field/widget.js +142 -0
- package/widgets/magic-timer/styles.js +30 -0
- package/widgets/magic-timer/widget.js +162 -0
- package/widgets/magic-zen/styles.js +61 -0
- package/widgets/magic-zen/widget.js +42 -0
- package/widgets/main-tabs/styles.js +106 -0
- package/widgets/main-tabs/widget.js +23 -0
- package/widgets/menu/styles.js +156 -0
- package/widgets/menu/test-menu.html +154 -0
- package/widgets/menu/widget.js +575 -0
- package/widgets/movable/widget.js +80 -0
- package/widgets/splitter/styles.js +57 -0
- package/widgets/splitter/widget.js +40 -0
- package/widgets/tab-layout/styles.js +31 -0
- package/widgets/tab-layout/widget.js +59 -0
- package/widgets/with-computed-size/widget.js +52 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import * as styles from './styles.js';
|
|
4
|
+
import withC from 'goblin-laboratory/widgets/connect-helpers/with-c';
|
|
5
|
+
import MagicTextField from '../magic-text-field/widget.js';
|
|
6
|
+
import {number as NumberConverters} from 'xcraft-core-converters';
|
|
7
|
+
import InputGroup from '../input-group/widget.js';
|
|
8
|
+
import MagicButton from '../magic-button/widget.js';
|
|
9
|
+
import Icon from '@mdi/react';
|
|
10
|
+
import {mdiMinus, mdiPlus} from '@mdi/js';
|
|
11
|
+
|
|
12
|
+
class MagicNumberFieldNC extends Widget {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.styles = styles;
|
|
16
|
+
this.inputRef = this.props.inputRef || React.createRef();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get input() {
|
|
20
|
+
return this.inputRef.current;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
parse = (value) => {
|
|
24
|
+
const {min, max} = this.props;
|
|
25
|
+
const edited = NumberConverters.parseEdited(value, min, max);
|
|
26
|
+
return edited.value;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
format = (value) => {
|
|
30
|
+
if (!value && value !== 0) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
return NumberConverters.getDisplayed(value);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
increase = () => {
|
|
37
|
+
const {value, max, step = 1} = this.props;
|
|
38
|
+
if (max === undefined || value < max) {
|
|
39
|
+
this.props.onChange?.(value + step);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
decrease = () => {
|
|
44
|
+
const {value, min, step = 1} = this.props;
|
|
45
|
+
if (min === undefined || value > min) {
|
|
46
|
+
this.props.onChange?.(value - step);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
handleKeyDown = (event) => {
|
|
51
|
+
this.props.onKeyDown?.(event);
|
|
52
|
+
|
|
53
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
54
|
+
const {max, min, step = 1} = this.props;
|
|
55
|
+
const value = this.parse(event.target.value);
|
|
56
|
+
const newStep = event.shiftKey ? 10 * step : step;
|
|
57
|
+
const direction = event.key === 'ArrowUp' ? 1 : -1;
|
|
58
|
+
let newValue = value + newStep * direction;
|
|
59
|
+
if (max !== undefined && newValue > max) {
|
|
60
|
+
newValue = max;
|
|
61
|
+
}
|
|
62
|
+
if (min !== undefined && newValue < min) {
|
|
63
|
+
newValue = min;
|
|
64
|
+
}
|
|
65
|
+
if (newValue !== value) {
|
|
66
|
+
const text = this.format(newValue);
|
|
67
|
+
this.input.changeAndSelect(
|
|
68
|
+
text,
|
|
69
|
+
event.shiftKey ? 0 : text.length,
|
|
70
|
+
text.length
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
render() {
|
|
79
|
+
const {className = '', ...props} = this.props;
|
|
80
|
+
return (
|
|
81
|
+
<InputGroup>
|
|
82
|
+
<MagicTextField
|
|
83
|
+
inputRef={this.inputRef}
|
|
84
|
+
format={this.format}
|
|
85
|
+
parse={this.parse}
|
|
86
|
+
{...props}
|
|
87
|
+
onKeyDown={this.handleKeyDown}
|
|
88
|
+
className={this.styles.classNames.numberField + ' ' + className}
|
|
89
|
+
/>
|
|
90
|
+
<MagicButton onClick={this.decrease} disabled={props.disabled}>
|
|
91
|
+
<Icon path={mdiMinus} size={0.8} />
|
|
92
|
+
</MagicButton>
|
|
93
|
+
<MagicButton onClick={this.increase} disabled={props.disabled}>
|
|
94
|
+
<Icon path={mdiPlus} size={0.8} />
|
|
95
|
+
</MagicButton>
|
|
96
|
+
</InputGroup>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const MagicNumberField = withC(MagicNumberFieldNC, {value: 'onChange'});
|
|
101
|
+
MagicNumberField.displayName = 'MagicNumberField';
|
|
102
|
+
|
|
103
|
+
export default MagicNumberField;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/******************************************************************************/
|
|
2
|
+
|
|
3
|
+
export default function styles() {
|
|
4
|
+
const action = {
|
|
5
|
+
padding: '4px',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const show = {
|
|
9
|
+
position: 'fixed',
|
|
10
|
+
top: '47px',
|
|
11
|
+
left: 0,
|
|
12
|
+
width: '50%',
|
|
13
|
+
height: 'auto',
|
|
14
|
+
transition: 'width 0.2s ease, opacity 0.2s ease',
|
|
15
|
+
// zIndex: 10000,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const hidden = {
|
|
19
|
+
...show,
|
|
20
|
+
left: '-2px',
|
|
21
|
+
width: '0%',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const content = {
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const hiddenContent = {
|
|
30
|
+
visibility: 'hidden',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const shortcut = {
|
|
34
|
+
fontSize: '80%',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const closeButton = {
|
|
38
|
+
'position': 'absolute',
|
|
39
|
+
'top': 0,
|
|
40
|
+
'right': 0,
|
|
41
|
+
'fontSize': '20px',
|
|
42
|
+
'padding': '7px 10px',
|
|
43
|
+
'opacity': 0.6,
|
|
44
|
+
'cursor': 'pointer',
|
|
45
|
+
':hover': {
|
|
46
|
+
opacity: 1,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
hiddenContent,
|
|
52
|
+
content,
|
|
53
|
+
action,
|
|
54
|
+
hidden,
|
|
55
|
+
show,
|
|
56
|
+
shortcut,
|
|
57
|
+
closeButton,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/******************************************************************************/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import * as styles from './styles.js';
|
|
4
|
+
import MagicButton from '../magic-button/widget.js';
|
|
5
|
+
import MagicDiv from '../magic-div/widget.js';
|
|
6
|
+
import Icon from '@mdi/react';
|
|
7
|
+
import {mdiClose} from '@mdi/js';
|
|
8
|
+
|
|
9
|
+
class MagicPanel extends Widget {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.styles = styles;
|
|
13
|
+
this.state = {
|
|
14
|
+
hidden: true,
|
|
15
|
+
};
|
|
16
|
+
this.togglePanel = this.togglePanel.bind(this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
componentDidMount() {
|
|
20
|
+
// Mousetrap.bind('ctrl+space', this.togglePanel);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
togglePanel() {
|
|
24
|
+
this.setState({hidden: !this.state.hidden});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
render() {
|
|
28
|
+
const classState = this.state.hidden ? 'hidden' : 'show';
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<div className={this.styles.classNames.action}>
|
|
32
|
+
<MagicButton onClick={this.togglePanel}>
|
|
33
|
+
Menu{' '}
|
|
34
|
+
<span className={this.styles.classNames.shortcut}>
|
|
35
|
+
(ctrl+space)
|
|
36
|
+
</span>
|
|
37
|
+
</MagicButton>
|
|
38
|
+
</div>
|
|
39
|
+
<MagicDiv className={this.styles.classNames[classState]}>
|
|
40
|
+
<div
|
|
41
|
+
className={
|
|
42
|
+
classState === 'hidden'
|
|
43
|
+
? this.styles.classNames.hiddenContent
|
|
44
|
+
: this.styles.classNames.content
|
|
45
|
+
}
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
className={this.styles.classNames.closeButton}
|
|
49
|
+
onClick={this.togglePanel}
|
|
50
|
+
>
|
|
51
|
+
<Icon path={mdiClose} />
|
|
52
|
+
</div>
|
|
53
|
+
{this.props.children}
|
|
54
|
+
</div>
|
|
55
|
+
</MagicDiv>
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/******************************************************************************/
|
|
62
|
+
|
|
63
|
+
export default MagicPanel;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/******************************************************************************/
|
|
2
|
+
|
|
3
|
+
export default function styles() {
|
|
4
|
+
const input = {
|
|
5
|
+
'display': 'flex',
|
|
6
|
+
'justifyContent': 'center',
|
|
7
|
+
'alignItems': 'center',
|
|
8
|
+
'width': '24px',
|
|
9
|
+
'height': '24px',
|
|
10
|
+
'margin': '1px 0',
|
|
11
|
+
'flexShrink': 0,
|
|
12
|
+
'position': 'relative',
|
|
13
|
+
// 'bottom': '1px',
|
|
14
|
+
'top': '1px',
|
|
15
|
+
'cursor': 'pointer',
|
|
16
|
+
|
|
17
|
+
'appearance': 'none',
|
|
18
|
+
'backgroundColor': 'color-mix(in srgb, var(--text-color), transparent 80%)',
|
|
19
|
+
'border':
|
|
20
|
+
'1px solid color-mix(in srgb, var(--text-color), transparent 60%)',
|
|
21
|
+
'borderRadius': '50%',
|
|
22
|
+
|
|
23
|
+
'&::after': {
|
|
24
|
+
content: "''",
|
|
25
|
+
display: 'block',
|
|
26
|
+
width: '16px',
|
|
27
|
+
height: '16px',
|
|
28
|
+
borderRadius: '50%',
|
|
29
|
+
backgroundColor: 'rgba(15, 26, 54, 0.8)',
|
|
30
|
+
opacity: 0,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
'&:checked::after': {
|
|
34
|
+
opacity: 1,
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
'&:focus-visible': {
|
|
38
|
+
borderColor: 'var(--text-color)',
|
|
39
|
+
boxShadow: '0px 0px 3px var(--text-color)',
|
|
40
|
+
outline: 'none',
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
'&:focus': {
|
|
44
|
+
outline: 'none',
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
'&:active': {
|
|
48
|
+
transform: 'scale(0.9)',
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
'&:disabled': {
|
|
52
|
+
'borderColor': 'transparent',
|
|
53
|
+
'backgroundColor':
|
|
54
|
+
'color-mix(in srgb, var(--text-color), transparent 90%)',
|
|
55
|
+
'&:hover': {
|
|
56
|
+
cursor: 'unset',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const radioLabel = {
|
|
62
|
+
'display': 'flex',
|
|
63
|
+
'alignItems': 'center',
|
|
64
|
+
// 'alignItems': 'baseline',
|
|
65
|
+
|
|
66
|
+
'&:hover input[type=radio]': {
|
|
67
|
+
'backgroundColor':
|
|
68
|
+
'color-mix(in srgb, var(--text-color), transparent 60%)',
|
|
69
|
+
|
|
70
|
+
'&:disabled': {
|
|
71
|
+
backgroundColor:
|
|
72
|
+
'color-mix(in srgb, var(--text-color), transparent 90%)',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const text = {
|
|
78
|
+
// 'display': 'flex',
|
|
79
|
+
// 'alignItems': 'baseline',
|
|
80
|
+
'marginLeft': '8px',
|
|
81
|
+
'&:empty': {
|
|
82
|
+
marginLeft: 0,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
radioLabel,
|
|
88
|
+
input,
|
|
89
|
+
text,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/******************************************************************************/
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as styles from './styles.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
4
|
+
import withC from 'goblin-laboratory/widgets/connect-helpers/with-c';
|
|
5
|
+
|
|
6
|
+
class InputRadio extends Widget {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.onChange = this.onChange.bind(this);
|
|
10
|
+
this.styles = styles;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onChange(e) {
|
|
14
|
+
this.props.onChange(e.target.value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render() {
|
|
18
|
+
const {name, value, checked, state, ...props} = this.props;
|
|
19
|
+
return (
|
|
20
|
+
<input
|
|
21
|
+
{...props}
|
|
22
|
+
type="radio"
|
|
23
|
+
name={name}
|
|
24
|
+
value={value}
|
|
25
|
+
checked={state !== undefined ? state === value : checked}
|
|
26
|
+
className={this.styles.classNames.input}
|
|
27
|
+
onChange={this.onChange}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class MagicRadioNC extends Widget {
|
|
34
|
+
constructor() {
|
|
35
|
+
super(...arguments);
|
|
36
|
+
this.styles = styles;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
render() {
|
|
40
|
+
const {
|
|
41
|
+
name,
|
|
42
|
+
value,
|
|
43
|
+
checked,
|
|
44
|
+
state,
|
|
45
|
+
onChange,
|
|
46
|
+
text,
|
|
47
|
+
title,
|
|
48
|
+
disabled,
|
|
49
|
+
children,
|
|
50
|
+
className = '',
|
|
51
|
+
...props
|
|
52
|
+
} = this.props;
|
|
53
|
+
return (
|
|
54
|
+
<label
|
|
55
|
+
className={this.styles.classNames.radioLabel + ' ' + className}
|
|
56
|
+
title={title}
|
|
57
|
+
>
|
|
58
|
+
<InputRadio
|
|
59
|
+
{...props}
|
|
60
|
+
name={name}
|
|
61
|
+
value={value}
|
|
62
|
+
checked={checked}
|
|
63
|
+
state={state}
|
|
64
|
+
onChange={onChange}
|
|
65
|
+
disabled={disabled}
|
|
66
|
+
/>
|
|
67
|
+
<span className={this.styles.classNames.text}>{text || children}</span>
|
|
68
|
+
</label>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const MagicRadio = withC(MagicRadioNC, {state: 'onChange'});
|
|
73
|
+
MagicRadio.displayName = 'MagicRadio';
|
|
74
|
+
export default MagicRadio;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default function styles() {
|
|
2
|
+
const scroll = {
|
|
3
|
+
'padding': '20px',
|
|
4
|
+
'overflow': 'auto',
|
|
5
|
+
|
|
6
|
+
'& > h1:first-child': {
|
|
7
|
+
marginTop: 0,
|
|
8
|
+
},
|
|
9
|
+
'& > h2:first-child': {
|
|
10
|
+
marginTop: 0,
|
|
11
|
+
},
|
|
12
|
+
'& > h3:first-child': {
|
|
13
|
+
marginTop: 0,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
scroll,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/******************************************************************************/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import * as styles from './styles.js';
|
|
4
|
+
|
|
5
|
+
export default class MagicScroll extends Widget {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.styles = styles;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
const {className = '', ...props} = this.props;
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={this.styles.classNames.scroll + ' ' + className}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import * as styles from './styles.js';
|
|
4
|
+
import Menu from '../menu/widget.js';
|
|
5
|
+
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|
6
|
+
import {faChevronDown} from '@fortawesome/free-solid-svg-icons';
|
|
7
|
+
import withC from 'goblin-laboratory/widgets/connect-helpers/with-c.js';
|
|
8
|
+
import C from 'goblin-laboratory/widgets/connect-helpers/c.js';
|
|
9
|
+
|
|
10
|
+
const SelectContext = React.createContext();
|
|
11
|
+
|
|
12
|
+
function examples() {
|
|
13
|
+
<MagicSelect value={C('.type')}>
|
|
14
|
+
<option value="external">External</option>
|
|
15
|
+
<option value="internal">Internal</option>
|
|
16
|
+
<option value="personal">Personal</option>
|
|
17
|
+
</MagicSelect>;
|
|
18
|
+
|
|
19
|
+
<MagicSelect
|
|
20
|
+
value={C('.type')}
|
|
21
|
+
options={{
|
|
22
|
+
external: <>External</>,
|
|
23
|
+
internal: <>Internal</>,
|
|
24
|
+
personal: <>Personal</>,
|
|
25
|
+
}}
|
|
26
|
+
/>;
|
|
27
|
+
|
|
28
|
+
<MagicSelect
|
|
29
|
+
value={C('.type')}
|
|
30
|
+
options={[
|
|
31
|
+
{value: 'external', text: <>External</>},
|
|
32
|
+
{value: 'internal', text: <>Internal</>},
|
|
33
|
+
{value: 'personal', text: <>Personal</>},
|
|
34
|
+
]}
|
|
35
|
+
/>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class MagicSelectOption extends Widget {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
this.styles = styles;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
const {value, children} = this.props;
|
|
46
|
+
return (
|
|
47
|
+
<SelectContext.Consumer>
|
|
48
|
+
{(select) => (
|
|
49
|
+
<Menu.Item value={value} onPointerUp={select.setValue}>
|
|
50
|
+
{children}
|
|
51
|
+
</Menu.Item>
|
|
52
|
+
)}
|
|
53
|
+
</SelectContext.Consumer>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class MagicSelectNC extends Widget {
|
|
59
|
+
constructor() {
|
|
60
|
+
super(...arguments);
|
|
61
|
+
this.styles = styles;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setValue = (event) => {
|
|
65
|
+
const value = event.currentTarget.value;
|
|
66
|
+
this.props.onChange?.(value, event);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
render() {
|
|
70
|
+
let {
|
|
71
|
+
className = '',
|
|
72
|
+
value,
|
|
73
|
+
options,
|
|
74
|
+
position,
|
|
75
|
+
onChange,
|
|
76
|
+
children,
|
|
77
|
+
...props
|
|
78
|
+
} = this.props;
|
|
79
|
+
|
|
80
|
+
let displayedValue = value;
|
|
81
|
+
let list;
|
|
82
|
+
if (options) {
|
|
83
|
+
list = Array.isArray(options)
|
|
84
|
+
? options
|
|
85
|
+
: Object.entries(options).map(([value, text]) => ({value, text}));
|
|
86
|
+
|
|
87
|
+
displayedValue = list.find((item) => item.value === value)?.text || value;
|
|
88
|
+
} else {
|
|
89
|
+
children = React.Children.map(children, (child) => {
|
|
90
|
+
if (React.isValidElement(child) && child.type === 'option') {
|
|
91
|
+
return <MagicSelectOption {...child.props} />;
|
|
92
|
+
}
|
|
93
|
+
return child;
|
|
94
|
+
});
|
|
95
|
+
React.Children.forEach(children, (child) => {
|
|
96
|
+
if (!React.isValidElement(child)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (child.type === MagicSelectOption && child.props.value === value) {
|
|
100
|
+
displayedValue = child.props.children;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<SelectContext.Provider value={this}>
|
|
107
|
+
<Menu>
|
|
108
|
+
<Menu.Button {...props} className={'input no-grow ' + className}>
|
|
109
|
+
{displayedValue}
|
|
110
|
+
<FontAwesomeIcon
|
|
111
|
+
className={this.styles.classNames.chevronDown}
|
|
112
|
+
icon={faChevronDown}
|
|
113
|
+
/>
|
|
114
|
+
</Menu.Button>
|
|
115
|
+
<Menu.Content position={position}>
|
|
116
|
+
{list &&
|
|
117
|
+
list.map(({value, text}) => (
|
|
118
|
+
<MagicSelectOption key={value} value={value}>
|
|
119
|
+
{text || value}
|
|
120
|
+
</MagicSelectOption>
|
|
121
|
+
))}
|
|
122
|
+
{children}
|
|
123
|
+
</Menu.Content>
|
|
124
|
+
</Menu>
|
|
125
|
+
</SelectContext.Provider>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const MagicSelect = withC(MagicSelectNC, {value: 'onChange'});
|
|
131
|
+
|
|
132
|
+
MagicSelect.Option = MagicSelectOption;
|
|
133
|
+
|
|
134
|
+
export default MagicSelect;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Shredder from 'xcraft-core-shredder';
|
|
2
|
+
/******************************************************************************/
|
|
3
|
+
|
|
4
|
+
const initialState = new Shredder({
|
|
5
|
+
id: null,
|
|
6
|
+
sortIndex: null,
|
|
7
|
+
sortType: null,
|
|
8
|
+
sortOrder: '-',
|
|
9
|
+
sortCustom: null,
|
|
10
|
+
showMenu: false,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/******************************************************************************/
|
|
14
|
+
|
|
15
|
+
export default (state = initialState, action = {}) => {
|
|
16
|
+
switch (action.type) {
|
|
17
|
+
case 'INITIALISE': {
|
|
18
|
+
const {id, columns} = action;
|
|
19
|
+
state = initialState;
|
|
20
|
+
state = state.set('id', id);
|
|
21
|
+
for (const [index, column] of columns.entries()) {
|
|
22
|
+
if (column.sortable && column.sortOrder) {
|
|
23
|
+
state = state.set('sortIndex', index);
|
|
24
|
+
state = state.set('sortType', column.type);
|
|
25
|
+
state = state.set('sortOrder', column.sortOrder);
|
|
26
|
+
state = state.set('sortCustom', column.sortCustom);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return state;
|
|
30
|
+
}
|
|
31
|
+
case 'TOGGLE_MENU': {
|
|
32
|
+
const {sortIndex, sortType, sortCustom} = action;
|
|
33
|
+
state = state.set('sortIndex', sortIndex);
|
|
34
|
+
state = state.set('sortType', sortType);
|
|
35
|
+
state = state.set('sortCustom', sortCustom);
|
|
36
|
+
state = state.set('showMenu', !state.get('showMenu'));
|
|
37
|
+
return state;
|
|
38
|
+
}
|
|
39
|
+
case 'SORT_COLUMN': {
|
|
40
|
+
const {sortIndex, sortType, sortCustom} = action;
|
|
41
|
+
const current = state.get('sortIndex');
|
|
42
|
+
state = state.set('sortIndex', sortIndex);
|
|
43
|
+
state = state.set('sortType', sortType);
|
|
44
|
+
state = state.set('sortCustom', sortCustom);
|
|
45
|
+
|
|
46
|
+
//set new asc sort for column
|
|
47
|
+
if (current !== sortIndex) {
|
|
48
|
+
state = state.set('sortOrder', 'asc');
|
|
49
|
+
return state;
|
|
50
|
+
}
|
|
51
|
+
//cycle sort
|
|
52
|
+
if (state.get('sortOrder') === '-') {
|
|
53
|
+
state = state.set('sortOrder', 'asc');
|
|
54
|
+
} else if (state.get('sortOrder') === 'asc') {
|
|
55
|
+
state = state.set('sortOrder', 'desc');
|
|
56
|
+
} else {
|
|
57
|
+
state = state.set('sortOrder', '-');
|
|
58
|
+
}
|
|
59
|
+
return state;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return state;
|
|
63
|
+
};
|