Skip to content

Commit

Permalink
Merge pull request #23 from lilingxi01/headless-input
Browse files Browse the repository at this point in the history
Finish building Headless Input and a demo of Headful Input
  • Loading branch information
lilingxi01 committed Jun 7, 2022
2 parents 8da562b + 6d2daa4 commit 8af169f
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 74 deletions.
61 changes: 51 additions & 10 deletions packages/date-picker/date-picker-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,30 @@ import { styled } from '@stitches/react';
import { parseDate } from './processor.js';
import { dateOptions } from '../support/date.js';

const AmsDatePickerInputContainer = styled('input', {});
const AmsDatePickerInputContainer = styled('input', {
outline: 'none',
border: 'none',
backgroundColor: 'transparent',
});

/**
* [Ams] The headless date picker component.
* @param {string} className
* @param {string} id
* @param {object} style
* @param {any} value
* @param {any} baseDate
* @param {function} onChange - Callback function to be called when the date is changed (only when finalized).
* @param {function} onError - Callback function when the error is occurring in user's input (neither functionality error nor development error).
* @param {function} onKeyPress
* @param {function} onFocus
* @param {function} onBlur
* @param {object} dateOption - (TBD) The date option for formatting the date.
* @param {function} onShouldOpenSelector
* @param {function} onShouldCloseSelector
* @param {any} props
* @return {JSX.Element}
*/
export const AmsDatePickerInput = ({
className,
id,
Expand All @@ -16,13 +38,19 @@ export const AmsDatePickerInput = ({
onKeyPress,
onFocus,
onBlur,
dateOption = dateOptions,
onShouldOpenSelector,
onShouldCloseSelector,
...props
}) => {
const [inputValue, setInputValue] = useState(value);
const [inputValue, setInputValue] = useState('');

useEffect(() => {
setInputValue(value.toLocaleString('en-US', dateOptions));
if (value) {
setInputValue(value.toLocaleString('en-US', dateOption));
} else {
setInputValue('');
}
}, [value]);

// This function is a callback when the input is finished by user (on finalizing or on blurring).
Expand All @@ -34,36 +62,45 @@ export const AmsDatePickerInput = ({
onChange(parsedDate);
}
} catch (e) {
onError(e); // Return error.
if (onError) {
onError(e); // Return error.
} else {
console.error('AmsDatePicker:', e); // Log error.
}
}
};

// This function is used to handle the close action of the date selector.
const handleCloseDateSelector = () => {
// TODO.
// This function is used to handle the close action of the date selector or the blur action of input.
const handleEscape = () => {
// TODO: Blur the input when needed.
// TODO: Call back the onShouldCloseSelector callback with some conditions.
};

// This function should be called to determine if we should finish the input on blur.
const isValidOnBlur = () => {
return (
inputValue.length > 0
&& !inputValue.match(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{2}(?::\d{2})? (?:AM|PM)?$/)
&& inputValue.match(/^\d{1,2}\/\d{1,2}\/\d{4},? \d{1,2}:\d{2}(?::\d{2})? ?(?:AM|PM)?$/)
);
};

// TODO: Make the input element style-less.
// This input element is style-less.
return (
<AmsDatePickerInputContainer
className={`ams-date-picker-input ${className ?? ''}`}
id={id ?? 'ams-date-picker-input'}
css={style}
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
}}
onKeyPress={(e) => {
if (e.key === 'Enter') {
onInputFinish(inputValue);
}
if (e.key === 'Escape') {
handleEscape();
}
if (onKeyPress) {
onKeyPress(e);
}
Expand All @@ -75,14 +112,18 @@ export const AmsDatePickerInput = ({
}
}}
onBlur={(e) => {
// TODO: Determine if we should close the data selector.
// Determine if we should close the data selector.
if (isValidOnBlur()) {
onInputFinish(inputValue);
}
if (onBlur) {
onBlur(e);
}
}}
{
...props
// TODO: Handle potential conflicts with props.
}
/>
);
};
94 changes: 56 additions & 38 deletions packages/date-picker/date-picker.js
Original file line number Diff line number Diff line change
@@ -1,83 +1,101 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { AmsDatePickerInput } from './date-picker-input';
import { Layout } from './layout-support.js';
import { AmsDesign } from '../support/standards.js';

/**
* [Ams] (WIP) Magic Date Picker Component.
*
* @param {string|undefined} className
* @param {string|undefined} id
* @param {string} label
* @param {Date|undefined} value
* @param {function} onChange
* @param {string|null} hint
* @param {string|null} error
* @param {Date|undefined} baseDate
* @param {object} layoutStyle
* @param {object} style
* @param {any} props
* @return {JSX.Element}
*/
export const AmsDatePicker = ({
className,
id = 'ams-date-picker',
label,
value,
onChange,
hint,
error,
baseDate,
layoutStyle,
style,
...props
}) => {
// Date picker value state.
const [valueState, setValueState] = useState(null);
const [valueState, setValueState] = useState(value);

// Date picker open state.
const [isOpen, setIsOpen] = useState(false);

// Date picker hint state.
const [hintState, setHintState] = useState(null);

// Date picker input field error state.
const [errorState, setErrorState] = useState(null);

// Date picker anchor element.
const boxRef = useRef(null);

const handlePopoverClose = () => {
setIsOpen(false);
};

// Update hint state when hint prop changes.
useEffect(() => {
if (hint) {
setHintState(hint);
}
}, [hint]);

// Update error state when error prop changes.
useEffect(() => {
if (error) {
setErrorState(error);
}
}, [error]);

// Process the datepicker value into input value.
useEffect(() => {
if (valueState) {
setHintState(null);
setErrorState(null);
if (onChange) {
// Call the onChange callback with Date object.
// It will always be called nevertheless it is inputted by typing or selecting.
onChange(valueState);
} else {
console.warn('ams:', 'onChange callback is not defined.');
console.log('ams:', 'valueState:', valueState);
}
}
}, [valueState]);

return (
<div
style={{
<Layout
id={id}
css={{
width: '100%',
height: '42px',
display: 'flex',
flexDirection: 'column',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
borderRadius: '$md',
border: '0.5px solid $gray5',
backgroundColor: '$gray1',
transition: AmsDesign.transition.cubic,
'&:hover': {
backgroundColor: '$white',
border: '0.5px solid $gray7',
},
'&:focus-within': {
backgroundColor: '$white',
boxShadow: '$md',
},
overflow: 'hidden',
...layoutStyle,
}}
>
{/* TODO */}
</div>
{/* TODO: Replenish necessary APIs */}
<AmsDatePickerInput
className={className}
id={`${id}-input`}
value={valueState}
baseDate={baseDate}
onChange={(value) => {
setValueState(value);
}}
style={{
width: '100%',
height: '100%',
padding: '10px 14px',
fontSize: '$lg',
fontWeight: '400',
...style,
}}
{...props}
/>
</Layout>
);
};
4 changes: 2 additions & 2 deletions packages/support/date.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const dateOptions = {
year: 'numeric',
month: 'numeric',
day: 'numeric',
month: '2-digit',
day: '2-digit',
weekday: undefined,
hour: 'numeric',
minute: 'numeric',
Expand Down
8 changes: 4 additions & 4 deletions packages/support/stitches.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export const {
black: '#000',
},
shadows: {
'sm': '0 5px 6px 0 rgba(0, 0, 0, 0.03)',
'md': '0 6px 8px 0 rgba(0, 0, 0, 0.03)',
'lg': '0px 10px 16px rgba(0, 0, 0, 0.03)',
'sm': '0 4px 10px 0 rgba(0, 0, 0, 0.03)',
'md': '0 6px 14px 0 rgba(0, 0, 0, 0.03)',
'lg': '0px 10px 20px rgba(0, 0, 0, 0.03)',
},
fontSizes: {
'xxs': '11px',
Expand Down Expand Up @@ -68,7 +68,7 @@ export const {
},
});

export const amsDarkTheme = createTheme('ams-dark-theme', {
export const amsDarkTheme = createTheme('dark', {
colors: {
...grayDark,
...blueDark,
Expand Down
6 changes: 4 additions & 2 deletions packages/user-manual/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { styled } from '@stitches/react';
import * as Dialog from '@radix-ui/react-dialog';
import { IconQuestionMark } from '@tabler/icons';
import { IconHelp, IconQuestionMark } from '@tabler/icons';

import { AmsDesign } from '../support/standards.js';

Expand Down Expand Up @@ -71,7 +71,9 @@ export const AmsUserManual = ({ style, children }) => {
css={style}
>
{children ?? (
<IconQuestionMark size={24} />
<IconHelp size={17} strokeWidth={2.1} style={{
marginBottom: -2,
}}/>
)}
</UMTrigger>
<Dialog.Portal>
Expand Down
Loading

0 comments on commit 8af169f

Please sign in to comment.