@dtdot/lego 2.0.0-12 → 2.0.0-14
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/build/components/ControlLine/ControlLine.component.d.ts +7 -0
- package/build/components/ControlLine/ControlLine.component.js +33 -0
- package/build/components/Input/Input.component.d.ts +2 -0
- package/build/components/Input/Input.component.js +22 -5
- package/build/components/Select/Select.component.d.ts +1 -4
- package/build/components/Select/Select.component.js +8 -43
- package/build/components/common/Options.component.d.ts +19 -0
- package/build/components/common/Options.component.js +43 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/shared/ControlStyles.js +1 -1
- package/build/theme/dark.theme.js +4 -2
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
const ActionContainer = styled.div `
|
|
4
|
+
display: flex;
|
|
5
|
+
width: 100%;
|
|
6
|
+
|
|
7
|
+
> :first-child {
|
|
8
|
+
flex-grow: 1;
|
|
9
|
+
padding-right: 3px;
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
const SpacedContainer = styled.div `
|
|
13
|
+
display: flex;
|
|
14
|
+
|
|
15
|
+
> * {
|
|
16
|
+
flex-grow: 1;
|
|
17
|
+
padding-right: 3px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
> *:last-child {
|
|
21
|
+
padding-right: 0;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const ControlLine = ({ children, variant = 'action' }) => {
|
|
25
|
+
if (variant === 'action') {
|
|
26
|
+
return React.createElement(ActionContainer, null, children);
|
|
27
|
+
}
|
|
28
|
+
if (variant === 'spaced') {
|
|
29
|
+
return React.createElement(SpacedContainer, null, children);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
export default ControlLine;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { SelectOption } from '../common/Options.component';
|
|
2
3
|
export interface IInputProps {
|
|
3
4
|
'name'?: string;
|
|
4
5
|
'label'?: string;
|
|
@@ -13,6 +14,7 @@ export interface IInputProps {
|
|
|
13
14
|
'onFocus'?: () => void;
|
|
14
15
|
'onBlur'?: () => void;
|
|
15
16
|
'data-cy'?: string;
|
|
17
|
+
'suggestions'?: SelectOption[];
|
|
16
18
|
}
|
|
17
19
|
declare const Input: React.ForwardRefExoticComponent<IInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
18
20
|
export default Input;
|
|
@@ -8,9 +8,10 @@ import ControlDescription from '../../shared/ControlDescription';
|
|
|
8
8
|
import ControlLabel from '../../shared/ControlLabel';
|
|
9
9
|
import { ControlStyles } from '../../shared/ControlStyles';
|
|
10
10
|
import useFormNode, { getValue } from '../Form/useFormNode.hook';
|
|
11
|
+
import { OptionsPopper } from '../common/Options.component';
|
|
11
12
|
const InputContainer = styled.div `
|
|
12
13
|
position: relative;
|
|
13
|
-
|
|
14
|
+
border-radius: 2px;
|
|
14
15
|
|
|
15
16
|
background-color: ${(props) => props.theme.colours.controlBackground};
|
|
16
17
|
`;
|
|
@@ -62,11 +63,22 @@ const messageVariants = {
|
|
|
62
63
|
errorFocus: { opacity: 1, y: -4 },
|
|
63
64
|
};
|
|
64
65
|
const Input = React.forwardRef(function ForwardRefInput(props, ref) {
|
|
65
|
-
const { label, name, description, placeholder, disabled, type = 'text', autoFocus, value, 'error': propsError, onChange, onFocus, onBlur, 'data-cy': dataCy, } = props;
|
|
66
|
+
const { label, name, description, placeholder, disabled, type = 'text', autoFocus, value, 'error': propsError, onChange, onFocus, onBlur, 'data-cy': dataCy, suggestions, } = props;
|
|
67
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
66
68
|
const [isFocused, setIsFocused] = useState(false);
|
|
69
|
+
const [referenceElement, setReferenceElement] = useState();
|
|
67
70
|
const { value: contextValue, error: contextError, onChange: contextOnChange } = useFormNode(name);
|
|
68
71
|
const error = contextError || propsError;
|
|
69
72
|
const splitDescription = description ? description.split('\\n').map((str) => str.trim()) : undefined;
|
|
73
|
+
const handleSetSuggestion = (value) => {
|
|
74
|
+
setIsOpen(false);
|
|
75
|
+
if (onChange) {
|
|
76
|
+
onChange(value);
|
|
77
|
+
}
|
|
78
|
+
if (contextOnChange) {
|
|
79
|
+
contextOnChange(value);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
70
82
|
const handleChange = (e) => {
|
|
71
83
|
if (onChange) {
|
|
72
84
|
onChange(e.target.value);
|
|
@@ -87,17 +99,22 @@ const Input = React.forwardRef(function ForwardRefInput(props, ref) {
|
|
|
87
99
|
onBlur();
|
|
88
100
|
}
|
|
89
101
|
};
|
|
102
|
+
const handleClick = () => {
|
|
103
|
+
setIsOpen(true);
|
|
104
|
+
};
|
|
90
105
|
const animationVariant = error ? (isFocused ? 'errorFocus' : 'error') : undefined;
|
|
106
|
+
const filteredOptions = suggestions?.filter((option) => !value || option.label.toLowerCase().includes(value.toLowerCase()));
|
|
91
107
|
return (React.createElement("div", null,
|
|
92
108
|
label && React.createElement(ControlLabel, { htmlFor: name }, label),
|
|
93
|
-
React.createElement(InputContainer, { "data-cy": dataCy },
|
|
94
|
-
React.createElement(StyledInput, { ref: ref, animate: animationVariant, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, disabled: disabled, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, autoFocus: autoFocus, "data-cy": 'input' }),
|
|
109
|
+
React.createElement(InputContainer, { "data-cy": dataCy, ref: setReferenceElement },
|
|
110
|
+
React.createElement(StyledInput, { ref: ref, animate: animationVariant, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, disabled: disabled, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, onClick: handleClick, autoFocus: autoFocus, "data-cy": 'input' }),
|
|
95
111
|
React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-indicator' },
|
|
96
112
|
React.createElement(ErrorInner, null,
|
|
97
113
|
React.createElement(FontAwesomeIcon, { icon: faExclamationCircle }))),
|
|
98
114
|
error && (React.createElement(ErrorMessage, { style: { opacity: 0, y: 0 }, animate: animationVariant, variants: messageVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-message' }, error))),
|
|
99
115
|
splitDescription && (React.createElement(ControlDescription, null, splitDescription.map((line, index) => (React.createElement(React.Fragment, null,
|
|
100
116
|
index !== 0 && React.createElement("br", null),
|
|
101
|
-
line)))))
|
|
117
|
+
line))))),
|
|
118
|
+
filteredOptions && isOpen && (React.createElement(OptionsPopper, { referenceElement: referenceElement, options: filteredOptions, onSelect: handleSetSuggestion, onClose: () => setIsOpen(false) }))));
|
|
102
119
|
});
|
|
103
120
|
export default Input;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
2
|
-
import React, {
|
|
3
|
-
import ReactDOM from 'react-dom';
|
|
4
|
-
import { usePopper } from 'react-popper';
|
|
2
|
+
import React, { Fragment, useState } from 'react';
|
|
5
3
|
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
|
|
6
|
-
import
|
|
7
|
-
import styled, { useTheme } from 'styled-components';
|
|
4
|
+
import styled from 'styled-components';
|
|
8
5
|
import ControlDescription from '../../shared/ControlDescription';
|
|
9
6
|
import ControlLabel from '../../shared/ControlLabel';
|
|
10
7
|
import { ControlStyles } from '../../shared/ControlStyles';
|
|
11
8
|
import getThemeControlColours from '../../theme/helpers/getThemeControlColours';
|
|
12
9
|
import useFormNode, { getValue } from '../Form/useFormNode.hook';
|
|
10
|
+
import { OptionsPopper } from '../common/Options.component';
|
|
13
11
|
const ControlOuter = styled.div `
|
|
14
12
|
position: relative;
|
|
15
13
|
`;
|
|
@@ -37,54 +35,23 @@ const PlaceholderText = styled.div `
|
|
|
37
35
|
const ValueText = styled.div `
|
|
38
36
|
color: ${(props) => getThemeControlColours(props.theme).font};
|
|
39
37
|
`;
|
|
40
|
-
const OptionsContainer = styled.div `
|
|
41
|
-
width: 100%;
|
|
42
|
-
/* position: absolute; */
|
|
43
|
-
background-color: ${(props) => props.theme.colours.controlBackground};
|
|
44
|
-
z-index: 10000;
|
|
45
|
-
|
|
46
|
-
box-shadow: ${(props) => props.theme.shadows.small};
|
|
47
|
-
`;
|
|
48
|
-
const Option = styled(motion.div) `
|
|
49
|
-
color: ${(props) => getThemeControlColours(props.theme).font};
|
|
50
|
-
background-color: ${(props) => props.theme.colours.controlBackgroundDisabled};
|
|
51
|
-
height: 36px;
|
|
52
|
-
display: flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
padding: 0 12px;
|
|
55
|
-
cursor: pointer;
|
|
56
|
-
`;
|
|
57
38
|
const Select = (props) => {
|
|
58
|
-
const theme = useTheme();
|
|
59
39
|
const [isOpen, setIsOpen] = useState(false);
|
|
60
40
|
const [referenceElement, setReferenceElement] = useState();
|
|
61
|
-
const [popperElement, setPopperElement] = useState();
|
|
62
41
|
const { label, name, description, placeholder, 'value': propsValue, 'data-cy': dataCy, options } = props;
|
|
63
42
|
const { value: contextValue, onChange: contextOnChange } = useFormNode(name);
|
|
64
|
-
const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: 'bottom-start' });
|
|
65
43
|
const value = getValue(propsValue, contextValue);
|
|
66
44
|
const splitDescription = description ? description.split('\\n').map((str) => str.trim()) : undefined;
|
|
67
|
-
const
|
|
45
|
+
const handleSelect = (value) => {
|
|
68
46
|
setIsOpen(false);
|
|
69
47
|
if (contextOnChange) {
|
|
70
|
-
contextOnChange(
|
|
48
|
+
contextOnChange(value);
|
|
71
49
|
}
|
|
72
50
|
if (props.onChange) {
|
|
73
|
-
props.onChange(
|
|
51
|
+
props.onChange(value);
|
|
74
52
|
}
|
|
75
53
|
};
|
|
76
54
|
const valueLabel = value && options.find((o) => o.value === value)?.label;
|
|
77
|
-
const handleGlobalClick = useCallback((event) => {
|
|
78
|
-
if (!popperElement?.contains(event.target)) {
|
|
79
|
-
setIsOpen(false);
|
|
80
|
-
}
|
|
81
|
-
}, [setIsOpen, popperElement]);
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
document.addEventListener('mouseup', handleGlobalClick);
|
|
84
|
-
return () => {
|
|
85
|
-
document.removeEventListener('mouseup', handleGlobalClick);
|
|
86
|
-
};
|
|
87
|
-
}, [handleGlobalClick, popperElement]);
|
|
88
55
|
return (React.createElement("div", null,
|
|
89
56
|
label && React.createElement(ControlLabel, { htmlFor: name }, label),
|
|
90
57
|
React.createElement(ControlOuter, { ref: setReferenceElement },
|
|
@@ -94,10 +61,8 @@ const Select = (props) => {
|
|
|
94
61
|
value && React.createElement(ValueText, null, valueLabel)),
|
|
95
62
|
React.createElement(IconContainer, null,
|
|
96
63
|
React.createElement(FontAwesomeIcon, { icon: isOpen ? faChevronUp : faChevronDown }))),
|
|
97
|
-
isOpen &&
|
|
98
|
-
|
|
99
|
-
React.createElement(OptionsContainer, null, options.map((option) => (React.createElement(Option, { whileHover: { backgroundColor: theme.colours.controlBorder }, transition: { type: 'spring', duration: 0.2 }, key: option.value, onClick: () => selectValue(option) }, option.label))))), document.querySelector('body'))),
|
|
100
|
-
splitDescription && (React.createElement(ControlDescription, null, splitDescription.map((line, index) => (React.createElement(React.Fragment, null,
|
|
64
|
+
isOpen && (React.createElement(OptionsPopper, { referenceElement: referenceElement, options: options, onSelect: handleSelect, onClose: () => setIsOpen(false) }))),
|
|
65
|
+
splitDescription && (React.createElement(ControlDescription, null, splitDescription.map((line, index) => (React.createElement(Fragment, { key: index },
|
|
101
66
|
index !== 0 && React.createElement("br", null),
|
|
102
67
|
line)))))));
|
|
103
68
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare const OptionsContainer: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {}, never>;
|
|
3
|
+
export declare const Option: import("styled-components").StyledComponent<import("framer-motion").ForwardRefComponent<HTMLDivElement, import("framer-motion").HTMLMotionProps<"div">>, import("styled-components").DefaultTheme, {}, never>;
|
|
4
|
+
export type SelectOption = {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
};
|
|
8
|
+
export interface SelectOptionsProps {
|
|
9
|
+
options: SelectOption[];
|
|
10
|
+
onSelect: (value: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const SelectOptions: ({ options, onSelect }: SelectOptionsProps) => JSX.Element;
|
|
13
|
+
export interface SelectOptionsPopperProps {
|
|
14
|
+
referenceElement: any;
|
|
15
|
+
options: SelectOption[];
|
|
16
|
+
onSelect: (value: string) => void;
|
|
17
|
+
onClose: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const OptionsPopper: ({ referenceElement, options, onSelect, onClose }: SelectOptionsPopperProps) => React.ReactPortal;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import { usePopper } from 'react-popper';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import styled, { useTheme } from 'styled-components';
|
|
6
|
+
import getThemeControlColours from '../../theme/helpers/getThemeControlColours';
|
|
7
|
+
export const OptionsContainer = styled.div `
|
|
8
|
+
width: 100%;
|
|
9
|
+
background-color: ${(props) => props.theme.colours.controlBackground};
|
|
10
|
+
z-index: 10000;
|
|
11
|
+
|
|
12
|
+
box-shadow: ${(props) => props.theme.shadows.small};
|
|
13
|
+
`;
|
|
14
|
+
export const Option = styled(motion.div) `
|
|
15
|
+
color: ${(props) => getThemeControlColours(props.theme).font};
|
|
16
|
+
background-color: ${(props) => props.theme.colours.controlBackgroundDisabled};
|
|
17
|
+
height: 36px;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 0 12px;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
`;
|
|
23
|
+
export const SelectOptions = ({ options, onSelect }) => {
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
return (React.createElement(OptionsContainer, null, options.map((option) => (React.createElement(Option, { whileHover: { backgroundColor: theme.colours.controlBorder }, transition: { type: 'spring', duration: 0.2 }, key: option.value, onClick: () => onSelect(option.value) }, option.label)))));
|
|
26
|
+
};
|
|
27
|
+
export const OptionsPopper = ({ referenceElement, options, onSelect, onClose }) => {
|
|
28
|
+
const [popperElement, setPopperElement] = useState();
|
|
29
|
+
const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: 'bottom-start' });
|
|
30
|
+
const handleGlobalClick = useCallback((event) => {
|
|
31
|
+
if (!popperElement?.contains(event.target)) {
|
|
32
|
+
onClose();
|
|
33
|
+
}
|
|
34
|
+
}, [onClose, popperElement]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
document.addEventListener('mouseup', handleGlobalClick);
|
|
37
|
+
return () => {
|
|
38
|
+
document.removeEventListener('mouseup', handleGlobalClick);
|
|
39
|
+
};
|
|
40
|
+
}, [handleGlobalClick, popperElement]);
|
|
41
|
+
return ReactDOM.createPortal(React.createElement("div", { ref: setPopperElement, style: { ...styles.popper, zIndex: 999, width: referenceElement?.offsetWidth }, ...attributes.popper },
|
|
42
|
+
React.createElement(SelectOptions, { options: options, onSelect: onSelect })), document.querySelector('body'));
|
|
43
|
+
};
|
package/build/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { default as Card } from './components/Card/Card.component';
|
|
|
12
12
|
export { default as CardGroup } from './components/Card/CardGroup.component';
|
|
13
13
|
export { default as Checklist } from './components/Checklist/Checklist.component';
|
|
14
14
|
export { default as ControlGroup } from './components/ControlGroup/ControlGroup.component';
|
|
15
|
+
export { default as ControlLine } from './components/ControlLine/ControlLine.component';
|
|
15
16
|
export { default as FancyCheckbox } from './components/FancyCheckbox/FancyCheckbox.component';
|
|
16
17
|
export { default as Form } from './components/Form/Form.component';
|
|
17
18
|
export { default as Notification } from './components/Notification/Notification.component';
|
package/build/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { default as Card } from './components/Card/Card.component';
|
|
|
12
12
|
export { default as CardGroup } from './components/Card/CardGroup.component';
|
|
13
13
|
export { default as Checklist } from './components/Checklist/Checklist.component';
|
|
14
14
|
export { default as ControlGroup } from './components/ControlGroup/ControlGroup.component';
|
|
15
|
+
export { default as ControlLine } from './components/ControlLine/ControlLine.component';
|
|
15
16
|
export { default as FancyCheckbox } from './components/FancyCheckbox/FancyCheckbox.component';
|
|
16
17
|
export { default as Form } from './components/Form/Form.component';
|
|
17
18
|
export { default as Notification } from './components/Notification/Notification.component';
|
|
@@ -16,7 +16,7 @@ export const ControlStyles = css `
|
|
|
16
16
|
color: ${(props) => getThemeControlColours(props.theme).font};
|
|
17
17
|
background-color: ${(props) => getThemeControlColours(props.theme).background};
|
|
18
18
|
|
|
19
|
-
border: 1px solid ${(props) => getThemeControlColours(props.theme).
|
|
19
|
+
border: 1px solid ${(props) => getThemeControlColours(props.theme).background};
|
|
20
20
|
border-radius: 2px;
|
|
21
21
|
|
|
22
22
|
&:hover {
|
|
@@ -35,9 +35,11 @@ const darkTheme = {
|
|
|
35
35
|
faintBorder: '#cccccc1f',
|
|
36
36
|
controlBackground: '#5e6167',
|
|
37
37
|
controlBackgroundDisabled: '#47494d',
|
|
38
|
-
controlBorder: '#
|
|
38
|
+
controlBorder: '#595b5e',
|
|
39
|
+
// controlBorder: '#565656',
|
|
39
40
|
controlBorderFocus: '#4289de',
|
|
40
|
-
controlBorderHover: '#
|
|
41
|
+
controlBorderHover: '#686c74',
|
|
42
|
+
// controlBorderHover: '#4a4949',
|
|
41
43
|
controlPlaceholder: '#949494',
|
|
42
44
|
controlDescriptionColour: '#cbcbcb',
|
|
43
45
|
uploadBackground: '#5e6167',
|