diff --git a/package.json b/package.json index 98b5110b..f4769619 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.3.4", + "version": "0.4.0", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", @@ -55,11 +55,11 @@ "size-limit": [ { "path": "dist/components.cjs.production.min.js", - "limit": "100 KB" + "limit": "150 KB" }, { "path": "dist/components.esm.js", - "limit": "100 KB" + "limit": "150 KB" } ], "devDependencies": { @@ -79,11 +79,12 @@ "@types/react-transition-group": "^4.4.1", "babel-loader": "^8.1.0", "babel-plugin-polished": "^1.1.0", - "chromatic": "^5.2.0", + "chromatic": "^6.5.1", "husky": "^4.3.0", "polished": "^4.1.0", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-hook-form": "^7.27.1", "react-is": "^17.0.1", "size-limit": "^4.6.2", "storybook-addon-designs": "^5.4.5", @@ -107,6 +108,7 @@ "@react-aria/radio": "^3.1.3", "@react-aria/select": "^3.5.0", "@react-aria/separator": "^3.1.3", + "@react-aria/textfield": "^3.5.2", "@react-aria/tooltip": "^3.1.1", "@react-aria/utils": "^3.6.0", "@react-aria/virtualizer": "^3.3.4", diff --git a/src/Form.tsx b/src/Form.tsx deleted file mode 100644 index a2635bdd..00000000 --- a/src/Form.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { ReactNode } from 'react'; -import { css } from '@emotion/core'; -import theme from './theme'; - -export type FormProps = { - children: ReactNode; - className?: string; -}; - -function Form({ children, className }: FormProps) { - return ( -
- {children} -
- ); -} - -export const RequiredFieldsDisclosure = () => ( -
- * Required fields -
-); - -const formLabelText = css` - margin-bottom: 4px; - margin-top: 8px; - font-size: ${theme.typography.sizes.medium}; - color: ${theme.colors.text1}; -`; - -const formLabelHelper = css` - padding-left: 4px; - color: #bbbdbf; - font-size: ${theme.typography.sizes.medium}; -`; - -const formLabelContents = css` - margin-bottom: 8px; -`; - -export const FormLabel = ({ - children, - text, - required = false, - helperText, -}: { - children: ReactNode; - text: ReactNode; - required?: boolean; - helperText?: string; // Text the describe the field or display limits -}) => { - return ( - - ); -}; - -Form.RequiredFieldsDisclosure = RequiredFieldsDisclosure; -Form.FormLabel = FormLabel; - -export default Form; diff --git a/src/dropdown/DropdownButton.tsx b/src/dropdown/DropdownButton.tsx index abbbebf5..59455fec 100644 --- a/src/dropdown/DropdownButton.tsx +++ b/src/dropdown/DropdownButton.tsx @@ -8,27 +8,10 @@ import { FocusableRef } from '../types'; import { useFocusableRef } from '../utils/useDOMRef'; import theme from '../theme'; import { Text } from '../content'; +import { AddonBefore } from '../field'; import { Icon, ArrowIosDownwardOutline } from '../icon'; import { AddonableProps } from '../types'; -const addonBeforeCSS = css` - background-color: ${theme.colors.gray400}; - padding: ${theme.spacing.padding8}px; - flex: none; -`; - -/** - * A label element that describes the button - */ -function AddonBefore({ children }: { children: ReactNode }) { - return ( -
- - {children} - -
- ); -} export interface DropdownButtonProps extends AddonableProps { /** Whether the button is disabled. */ isDisabled?: boolean; @@ -66,7 +49,7 @@ function DropdownButton( background-color: ${theme.colors.gray500}; min-width: 200px; border: none; - border-radius: 4px; + border-radius: ${theme.borderRadius.medium}px; color: ${theme.textColors.white90}; display: flex; justify-content: center; @@ -78,8 +61,11 @@ function DropdownButton( transition: all 0.2s ease-in-out; /** provide an alternate highlight */ outline: none; + &.is-hovered { + border: 1px solid ${theme.components.dropdown.hoverBorderColor}; + background-color: ${theme.components.dropdown.activeBackgroundColor}; + } &.is-active, - &.is-hovered, &:focus { border: 1px solid ${theme.components.dropdown.activeBorderColor}; background-color: ${theme.components.dropdown.activeBackgroundColor}; diff --git a/src/field/Addon.tsx b/src/field/Addon.tsx new file mode 100644 index 00000000..2903043c --- /dev/null +++ b/src/field/Addon.tsx @@ -0,0 +1,27 @@ +import React, { ReactNode } from 'react'; +import { css } from '@emotion/core'; +import theme from '../theme'; +import { Text } from '../content/Text'; + +const addonBeforeCSS = css` + background-color: ${theme.colors.gray400}; + padding: 0 ${theme.spacing.padding8}px; + flex: none; + box-sizing: border-box; + height: ${theme.singleLineHeight}px; + display: flex; + align-items: center; +`; + +/** + * A label element that describes a button or an input field + */ +export function AddonBefore({ children }: { children: ReactNode }) { + return ( +
+ + {children} + +
+ ); +} diff --git a/src/field/Field.tsx b/src/field/Field.tsx new file mode 100644 index 00000000..2adced51 --- /dev/null +++ b/src/field/Field.tsx @@ -0,0 +1,137 @@ +import { css, keyframes } from '@emotion/core'; +import { classNames } from '../utils'; +import { HelpText, HelpTextComponentProps } from './HelpText'; +import { FieldLabel, FieldLabelProps } from './FieldLabel'; +import { LabelPosition } from '@react-types/shared'; +import { mergeProps } from '@react-aria/utils'; +import { Validation } from '../types'; +import React, { + RefObject, + HTMLAttributes, + ReactNode, + ReactElement, +} from 'react'; +import { useFormProps } from '../form'; + +const appearKeyframes = keyframes` + 0% { opacity: 0; } + 100% { opacity: 1; } +`; + +export interface FieldProps + extends FieldLabelProps, + HelpTextComponentProps, + Validation { + children: ReactElement; + label?: ReactNode; + labelProps?: HTMLAttributes; + wrapperClassName?: string; +} + +function Field(props: FieldProps, ref: RefObject) { + props = useFormProps(props); + let { + label, + labelExtra, + labelPosition = 'top' as LabelPosition, + labelAlign, + isRequired, + necessityIndicator, + includeNecessityIndicatorInAccessibilityName, + validationState, + description, + errorMessage, + isDisabled, + showErrorIcon, + children, + labelProps, + // Not every component that uses supports help text. + descriptionProps = {}, + errorMessageProps = {}, + elementType, + wrapperClassName, + } = props; + let hasHelpText = + !!description || (errorMessage && validationState === 'invalid'); + + if (label || hasHelpText) { + let labelWrapperClass = classNames( + 'ac-field', + { + 'ac-field--positionTop': labelPosition === 'top', + 'ac-field--positionSide': labelPosition === 'side', + 'ac-field--hasHelpText': hasHelpText, + }, + wrapperClassName + ); + + children = React.cloneElement( + children, + // @ts-ignore + mergeProps(children.props, { + className: 'ac-field__field', + }) + ); + + let renderHelpText = () => ( + + ); + + return ( +
} + className={labelWrapperClass} + css={css` + /* For now assume vertical alignment of labels */ + display: flex; + flex-direction: column; + align-items: flex-start; + .ac-help-text--danger { + /* Animate in the help text */ + animation: ${appearKeyframes} ${0.3}s forwards ease-in-out; + } + `} + > + {label && ( + + {label} + + )} + {children} + {hasHelpText && renderHelpText()} +
+ ); + } + + return React.cloneElement( + children, + // @ts-ignore + mergeProps(children.props, { + ref, + }) + ); +} + +// @ts-ignore +let _Field = React.forwardRef(Field); +export { _Field as Field }; diff --git a/src/field/FieldLabel.tsx b/src/field/FieldLabel.tsx new file mode 100644 index 00000000..de435db7 --- /dev/null +++ b/src/field/FieldLabel.tsx @@ -0,0 +1,103 @@ +import { classNames, useDOMRef } from '../utils'; +import { DOMRef } from '@react-types/shared'; +import { css } from '@emotion/core'; +import { filterDOMProps } from '@react-aria/utils'; +import React, { ReactNode, ElementType, HTMLAttributes } from 'react'; +import { DOMProps, LabelableProps, ExtendableLabelProps } from '../types'; +import theme from '../theme'; + +interface LabelProps { + children?: ReactNode; + htmlFor?: string; // for compatibility with React + for?: string; + elementType?: ElementType; +} + +interface FieldLabelPropsBase + extends LabelProps, + LabelableProps, + ExtendableLabelProps, + DOMProps { + includeNecessityIndicatorInAccessibilityName?: boolean; +} + +export interface FieldLabelProps + extends FieldLabelPropsBase, + HTMLAttributes {} + +function FieldLabel(props: FieldLabelProps, ref: DOMRef) { + let { + children, + labelPosition = 'top', + labelAlign = labelPosition === 'side' ? 'start' : null, + labelExtra, + isRequired, + necessityIndicator = 'icon', + includeNecessityIndicatorInAccessibilityName = false, + htmlFor, + for: labelFor, + elementType: ElementType = 'label', + onClick, + ...otherProps + } = props; + + let domRef = useDOMRef(ref); + let necessityLabel = isRequired ? '(required)' : '(optional)'; + let icon = '*'; + + let labelClassNames = classNames('ac-field-label', { + 'ac-field-label--positionSide': labelPosition === 'side', + 'ac-field-label--alignEnd': labelAlign === 'end', + }); + + return ( + + {children} + {(necessityIndicator === 'label' || + (necessityIndicator === 'icon' && isRequired)) && + ' \u200b'} + {/* necessityLabel is hidden to screen readers if the field is required because + * aria-required is set on the field in that case. That will already be announced, + * so no need to duplicate it here. If optional, we do want it to be announced here. */} + {necessityIndicator === 'label' && ( + + {necessityLabel} + + )} + {necessityIndicator === 'icon' && isRequired && icon} + {labelExtra && ( + + {labelExtra} + + )} + + ); +} + +let _FieldLabel = React.forwardRef(FieldLabel); +export { _FieldLabel as FieldLabel }; diff --git a/src/field/HelpText.tsx b/src/field/HelpText.tsx new file mode 100644 index 00000000..65dce7b4 --- /dev/null +++ b/src/field/HelpText.tsx @@ -0,0 +1,73 @@ +// import AlertMedium from '@spectrum-icons/ui/AlertMedium'; +import { classNames, useDOMRef } from '../utils'; +import { DOMRef, HelpTextProps, Validation } from '../types'; +import React, { HTMLAttributes } from 'react'; +import { css } from '@emotion/core'; +import theme from '../theme'; + +export interface HelpTextComponentProps extends HelpTextProps, Validation { + /** Props for the help text description element. */ + descriptionProps?: HTMLAttributes; + /** Props for the help text error message element. */ + errorMessageProps?: HTMLAttributes; + /** Whether the description is displayed with lighter text. */ + isDisabled?: boolean; + /** Whether an error icon is rendered. */ + showErrorIcon?: boolean; +} + +function HelpText(props: HelpTextComponentProps, ref: DOMRef) { + let { + description, + errorMessage, + validationState, + isDisabled, + showErrorIcon, + descriptionProps, + errorMessageProps, + } = props; + let domRef = useDOMRef(ref); + let isErrorMessage = errorMessage && validationState === 'invalid'; + + return ( +
+ {isErrorMessage ? ( + <> + {/* TODO support icons */} + {showErrorIcon && 'error icon here'} +
+ {errorMessage} +
+ + ) : ( +
+ {description} +
+ )} +
+ ); +} + +/** + * Help text provides either an informative description or an error message that gives more context about what a user needs to input. It's commonly used in forms. + */ +const _HelpText = React.forwardRef(HelpText); +export { _HelpText as HelpText }; diff --git a/src/field/index.tsx b/src/field/index.tsx new file mode 100644 index 00000000..bb7f4b70 --- /dev/null +++ b/src/field/index.tsx @@ -0,0 +1,2 @@ +export * from './Field'; +export * from './Addon'; diff --git a/src/form/Form.tsx b/src/form/Form.tsx new file mode 100644 index 00000000..f9e79344 --- /dev/null +++ b/src/form/Form.tsx @@ -0,0 +1,114 @@ +import { + Alignment, + DOMRef, + LabelPosition, + LabelableProps, + DOMProps, + AriaLabelingProps, +} from '../types'; +import { classNames, useDOMRef } from '../utils'; +import { filterDOMProps } from '@react-aria/utils'; +import React, { useContext, ReactElement, FormEventHandler } from 'react'; +import { css } from '@emotion/core'; +import { Provider } from '../provider'; + +let FormContext = React.createContext({}); + +export function useFormProps(props: T): T { + let ctx = useContext(FormContext); + return { ...ctx, ...props }; +} + +const formPropNames = new Set([ + 'action', + 'autoComplete', + 'encType', + 'method', + 'target', + 'onSubmit', +]); + +export interface FormProps extends DOMProps, AriaLabelingProps, LabelableProps { + /** The contents of the Form. */ + children: ReactElement | ReactElement[]; + + /** Whether the Form elements are disabled. */ + isDisabled?: boolean; + /** Whether the Form elements can be selected but not changed by the user. */ + isReadOnly?: boolean; + /** + * Where to send the form-data when the form is submitted. + */ + action?: string; + /** + * The enctype attribute specifies how the form-data should be encoded when submitting it to the server. + */ + encType?: + | 'application/x-www-form-urlencoded' + | 'multipart/form-data' + | 'text/plain'; + /** + * The form-data can be sent as URL variables (with method="get") or as HTTP post transaction (with method="post"). + */ + method?: 'get' | 'post'; + /** + * The target attribute specifies a name or a keyword that indicates where to display the response that is received after submitting the form. + */ + target?: '_blank' | '_self' | '_parent' | '_top'; + /** + * Fired on form submission. + */ + onSubmit?: FormEventHandler; +} + +function Form(props: FormProps, ref: DOMRef) { + let { + children, + labelPosition = 'top' as LabelPosition, + labelAlign = 'start' as Alignment, + isRequired, + necessityIndicator, + isDisabled, + isReadOnly, + ...otherProps + } = props; + let domRef = useDOMRef(ref); + + let ctx = { + labelPosition, + labelAlign, + necessityIndicator, + }; + + return ( +
+ + + {children} + + +
+ ); +} + +/** + * Forms allow users to enter data that can be submitted while providing alignment and styling for form fields. + */ +const _Form = React.forwardRef(Form); +export { _Form as Form }; diff --git a/src/form/index.tsx b/src/form/index.tsx new file mode 100644 index 00000000..b690c60a --- /dev/null +++ b/src/form/index.tsx @@ -0,0 +1 @@ +export * from './Form'; diff --git a/src/icon/Icon.tsx b/src/icon/Icon.tsx index 924827e8..ab928989 100644 --- a/src/icon/Icon.tsx +++ b/src/icon/Icon.tsx @@ -1,4 +1,5 @@ import React, { ReactNode, HTMLAttributes } from 'react'; +import { classNames } from '../utils'; import { css } from '@emotion/core'; interface IconProps extends HTMLAttributes { @@ -8,10 +9,10 @@ interface IconProps extends HTMLAttributes { /** * Wraps the svg in a reasonable size and applies a color */ -export const Icon = ({ svg, style, ...restProps }: IconProps) => { +export const Icon = ({ svg, style, className, ...restProps }: IconProps) => { return ( { // /** custom content, defaults to 'the snozzberries taste like snozzberries' */ // children?: ReactChild; diff --git a/src/provider/Provider.tsx b/src/provider/Provider.tsx new file mode 100644 index 00000000..62f75eca --- /dev/null +++ b/src/provider/Provider.tsx @@ -0,0 +1,37 @@ +import React, { useContext } from 'react'; +import { OverlayProvider } from '@react-aria/overlays'; +import { ProviderProps, ProviderContext } from '../types'; + +const Context = React.createContext(null); + +export function Provider(props: ProviderProps) { + const { children, ...context } = props; + return ( + + {children} + + ); +} + +export function useProvider(): ProviderContext { + const context = useContext(Context); + if (!context) { + throw new Error('useProvider must be used within a Provider'); + } + return context; +} + +export function useProviderProps(props: T): T { + let context = useProvider(); + if (!context) { + return props; + } + return Object.assign( + {}, + { + isDisabled: context.isDisabled, + isReadOnly: context.isReadOnly, + }, + props + ); +} diff --git a/src/provider/index.tsx b/src/provider/index.tsx new file mode 100644 index 00000000..50509410 --- /dev/null +++ b/src/provider/index.tsx @@ -0,0 +1 @@ +export * from './Provider'; diff --git a/src/textfield/TextField.tsx b/src/textfield/TextField.tsx new file mode 100644 index 00000000..679348b7 --- /dev/null +++ b/src/textfield/TextField.tsx @@ -0,0 +1,47 @@ +import React, { forwardRef, RefObject, useRef } from 'react'; +import { + TextFieldBase, + AriaTextFieldProps, + TextFieldRef, +} from './TextFieldBase'; +import { useTextField } from '@react-aria/textfield'; +import { AddonableProps } from '../types'; +import { useProviderProps } from '../provider'; + +export interface TextFieldProps extends AriaTextFieldProps, AddonableProps { + className?: string; +} +function TextField(props: TextFieldProps, ref: RefObject) { + // Call use provider props so the textfield can inherit from the provider + // E.x. disabled, readOnly, etc. + props = useProviderProps(props); + let inputRef = useRef(); + let { + labelProps, + inputProps, + descriptionProps, + errorMessageProps, + // @ts-ignore + } = useTextField(props, inputRef); + return ( + + ); +} + +/** + * TextFields are text inputs that allow users to input custom text entries + * with a keyboard. Various decorations can be displayed around the field to + * communicate the entry requirements. + */ +// @ts-ignore +const _TextField = forwardRef(TextField); +export { _TextField as TextField }; diff --git a/src/textfield/TextFieldBase.tsx b/src/textfield/TextFieldBase.tsx new file mode 100644 index 00000000..80e5184b --- /dev/null +++ b/src/textfield/TextFieldBase.tsx @@ -0,0 +1,284 @@ +import { css, keyframes } from '@emotion/core'; +import { classNames, createFocusableRef } from '../utils'; +import { Field } from '../field'; +import { FocusRing } from '@react-aria/focus'; +import { mergeProps } from '@react-aria/utils'; +import { PressEvents } from '@react-types/shared'; +import { Icon, AlertCircleOutline } from '../icon'; +import React, { + HTMLAttributes, + InputHTMLAttributes, + LabelHTMLAttributes, + ReactElement, + Ref, + RefObject, + TextareaHTMLAttributes, + useImperativeHandle, + useRef, + forwardRef, +} from 'react'; +import { + InputBase, + LabelableProps, + ExtendableLabelProps, + ValueBase, + TextInputBase, + FocusableProps, + HelpTextProps, + Validation, + AriaLabelingProps, + FocusableDOMProps, + TextInputDOMProps, + AriaValidationProps, + FocusableRefValue, + AddonableProps, +} from '../types'; +import { AddonBefore } from '../field'; +import { useHover } from '@react-aria/interactions'; +import theme from '../theme'; + +const appearKeyframes = keyframes` + 0% { opacity: 0; } + 100% { opacity: 1; } +`; + +export interface TextFieldProps + extends InputBase, + Validation, + HelpTextProps, + FocusableProps, + TextInputBase, + ValueBase, + LabelableProps, + ExtendableLabelProps {} + +/** + * Extend the base interface with a11y props + */ + +export interface AriaTextFieldProps + extends TextFieldProps, + AriaLabelingProps, + FocusableDOMProps, + TextInputDOMProps, + AriaValidationProps { + // https://www.w3.org/TR/wai-aria-1.2/#textbox + /** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */ + 'aria-activedescendant'?: string; + /** + * Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be + * presented if they are made. + */ + 'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both'; + /** Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element. */ + 'aria-haspopup'?: + | boolean + | 'false' + | 'true' + | 'menu' + | 'listbox' + | 'tree' + | 'grid' + | 'dialog'; +} + +interface TextFieldBaseProps + extends Omit, + AddonableProps, + PressEvents { + wrapperChildren?: ReactElement | ReactElement[]; + inputClassName?: string; + validationIconClassName?: string; + multiLine?: boolean; + labelProps?: LabelHTMLAttributes; + inputProps: + | InputHTMLAttributes + | TextareaHTMLAttributes; + descriptionProps?: HTMLAttributes; + errorMessageProps?: HTMLAttributes; + inputRef?: RefObject; + loadingIndicator?: ReactElement; + isLoading?: boolean; + className?: string; +} + +export interface TextFieldRef + extends FocusableRefValue< + HTMLInputElement | HTMLTextAreaElement, + HTMLDivElement + > { + select(): void; + getInputElement(): HTMLInputElement | HTMLTextAreaElement; +} + +function TextFieldBase(props: TextFieldBaseProps, ref: Ref) { + let { + label, + validationState, + isDisabled, + isReadOnly, + multiLine, + autoFocus, + inputClassName, + wrapperChildren, + labelProps = {}, + inputProps, + descriptionProps = {}, + errorMessageProps = {}, + inputRef, + isLoading, + loadingIndicator, + addonBefore, + className, + } = props; + let { hoverProps, isHovered } = useHover({ isDisabled }); + let [isFocused, setIsFocused] = React.useState(false); + let domRef = useRef(null); + let defaultInputRef = useRef(null); + inputRef = inputRef || defaultInputRef; + + // Expose imperative interface for ref + useImperativeHandle(ref, () => ({ + ...createFocusableRef(domRef, inputRef), + select() { + if (inputRef?.current) { + inputRef?.current.select(); + } + }, + // @ts-ignore + getInputElement() { + return inputRef?.current; + }, + })); + + let ElementType: React.ElementType = multiLine ? 'textarea' : 'input'; + let isInvalid = validationState === 'invalid'; + + const validation = ( + } + /> + ); + + let textField = ( +
+ {addonBefore != null ? {addonBefore} : null} + + { + setIsFocused(true); + }, + onBlur: () => setIsFocused(false), + })} + ref={inputRef as any} + rows={multiLine ? 1 : undefined} + className={classNames('ac-textfield__input', inputClassName)} + /> + + {validationState && !isLoading ? validation : null} + {isLoading && loadingIndicator} + {wrapperChildren} +
+ ); + + if (label) { + textField = React.cloneElement( + textField, + mergeProps(textField.props, { + // TODO support muli-line text areas + className: multiLine ? 'ac-text-field--multiline' : '', + }) + ); + } + + return ( + + {textField} + + ); +} + +const _TextFieldBase = forwardRef(TextFieldBase); +export { _TextFieldBase as TextFieldBase }; diff --git a/src/textfield/index.tsx b/src/textfield/index.tsx new file mode 100644 index 00000000..665fa3cb --- /dev/null +++ b/src/textfield/index.tsx @@ -0,0 +1 @@ +export * from './TextField'; diff --git a/src/theme.ts b/src/theme.ts index 1ce9cff4..665d6e53 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,3 +1,4 @@ +import { lighten, darken } from 'polished'; const baseColors = { dark1: '#0B1015', // body bg dark2: '#17202A', // nav bg @@ -25,6 +26,12 @@ const arizeColors = { arizeLightBlue: '#72D9FF', }; +const borderColors = { + grayBorder: '#2e363e', + lightGrayBorder: '#768CA3', + lightGrayHoverBorder: lighten(0.2, '#768CA3'), +}; + const textColors = { white90: `rgba(255, 255, 255, 0.9)`, white70: `rgba(255, 255, 255, 0.7)`, @@ -48,9 +55,9 @@ const theme = { ...baseColors, ...arizeColors, ...grayColors, + ...borderColors, primary: '#db247c', // pink secondary: '#db247c', // blue - grayBorder: '#2e363e', statusInfo: '#72D9FF', statusSuccess: '#7EE787', // RGB independent success color statusWarning: '#E69958', @@ -73,14 +80,22 @@ const theme = { borderColor: '#6F7D8C', }, dropdown: { - borderColor: 'rgba(118, 140, 163, .6)', - activeBorderColor: 'rgba(118, 140, 163, 1)', - activeBackgroundColor: '#313A44', + borderColor: borderColors.lightGrayBorder, + hoverBorderColor: borderColors.lightGrayHoverBorder, + activeBorderColor: arizeColors.arizeLightBlue, + activeBackgroundColor: darken(0.02, grayColors.gray500), + }, + textField: { + borderColor: borderColors.lightGrayBorder, + hoverBorderColor: borderColors.lightGrayHoverBorder, + activeBorderColor: arizeColors.arizeLightBlue, + backgroundColor: grayColors.gray500, + activeBackgroundColor: darken(0.02, grayColors.gray500), }, button: { primaryBorderColor: '#5BAECC', primaryHoverBackgroundColor: '#5BAECC', - defaultBorderColor: '#768CA3', + defaultBorderColor: borderColors.lightGrayBorder, defaultHoverBackgroundColor: '#64768A', }, }, @@ -154,6 +169,13 @@ const theme = { duration: 200, }, }, + /** + * The height of a single line of form input etc. + */ + singleLineHeight: 36, + borderRadius: { + medium: 4, + }, }; export default theme; diff --git a/src/types/index.ts b/src/types/index.ts index e88f5c22..b19b8a95 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -5,3 +5,7 @@ export * from './selection'; export * from './select'; export * from './labelable'; export * from './severity'; +export * from './input'; +export * from './events'; +export * from './dom'; +export * from './provider'; diff --git a/src/types/labelable.ts b/src/types/labelable.ts index 2740e9b1..a69559b3 100644 --- a/src/types/labelable.ts +++ b/src/types/labelable.ts @@ -28,6 +28,13 @@ export interface LabelableProps { isRequired?: boolean; } +export interface ExtendableLabelProps { + /** + * An extra element to add at the end of the label + */ + labelExtra?: ReactNode; +} + /** * An interface for things that support addon labels */ diff --git a/src/types/provider.ts b/src/types/provider.ts new file mode 100644 index 00000000..315512a1 --- /dev/null +++ b/src/types/provider.ts @@ -0,0 +1,15 @@ +import { ReactNode } from 'react'; + +interface ContextProps { + /** Whether descendants should be disabled. */ + isDisabled?: boolean; + /** Whether descendants should be read only. */ + isReadOnly?: boolean; +} + +export interface ProviderProps extends ContextProps { + /** The content of the Provider. */ + children: ReactNode; +} + +export interface ProviderContext extends ContextProps {} diff --git a/stories/Form.stories.tsx b/stories/Form.stories.tsx index 9bf7319a..699fbdaa 100644 --- a/stories/Form.stories.tsx +++ b/stories/Form.stories.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Meta, Story } from '@storybook/react'; -import Form, { FormProps } from '../src/Form'; +import { Form, FormProps, TextField, Picker, Item, Field } from '../src'; +import { useForm, Controller } from 'react-hook-form'; const meta: Meta = { title: 'Form', @@ -19,23 +20,84 @@ const meta: Meta = { export default meta; -const Template: Story = args => ( -
- - - - - - - - - - - -); +const Template: Story = args => { + const { control, handleSubmit } = useForm(); + const onSubmit = (d: any) => { + alert(JSON.stringify(d)); + }; + return ( + // @ts-ignore +
+ ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + + ( + + Free + Paid + + )} + /> + + + + ); +}; // By passing using the Args format for exported stories, you can control the props for a component for reuse in a test // https://storybook.js.org/docs/react/workflows/unit-testing export const Default = Template.bind({}); +export const Disabled = Template.bind({}, { isDisabled: true }); + Default.args = { type: 'primary', children: 'Button' }; diff --git a/stories/Gallery.stories.tsx b/stories/Gallery.stories.tsx index f1f6c74f..c9eb2be3 100644 --- a/stories/Gallery.stories.tsx +++ b/stories/Gallery.stories.tsx @@ -1,8 +1,16 @@ import React from 'react'; import { css } from '@emotion/core'; import { Meta, Story } from '@storybook/react'; -import { Alert, Card, useNotification, Button } from '../src'; - +import { + Alert, + Card, + useNotification, + Button, + TextField, + Dropdown, + Provider, + Field, +} from '../src'; // @ts-ignore import chartFile from './images/chart.png'; @@ -23,54 +31,95 @@ export default meta; const Template: Story = args => { const [notify, holder] = useNotification(); return ( -
- { - notify({ - variant: 'success', - title: 'Awesome!', - message: 'Things worked as expected', - action: { - text: 'Try This', - onClick: () => {}, - }, - }); - }} - > - Notify - - } + +
-
{ + notify({ + variant: 'success', + title: 'Awesome!', + message: 'Things worked as expected', + action: { + text: 'Try This', + onClick: () => {}, + }, + }); + }} + > + Notify + + } > - - Your predictions may be delayed by up to 10 minutes - +
+ + Your predictions may be delayed by up to 10 minutes + - chart image +
+ + +
-
-
- {holder} -
+ > + + + + Hello
}>Action + + +
+ + {holder} + + ); }; diff --git a/stories/Picker.stories.tsx b/stories/Picker.stories.tsx index cea9ec83..a58060ae 100644 --- a/stories/Picker.stories.tsx +++ b/stories/Picker.stories.tsx @@ -162,3 +162,36 @@ const Gallery: Story = () => { // By passing using the Args format for exported stories, you can control the props for a component for reuse in a test // https://storybook.js.org/docs/react/workflows/unit-testing export const gallery = Gallery.bind({}); + +const ItemsViaProps: Story> = args => { + const [frequency, setFrequency] = React.useState('rarely'); + return ( + + setFrequency(selected as string)} + items={[ + { id: 1, name: 'Aardvark' }, + { id: 2, name: 'Cat' }, + { id: 3, name: 'Dog' }, + { id: 4, name: 'Kangaroo' }, + { id: 5, name: 'Koala' }, + { id: 6, name: 'Penguin' }, + { id: 7, name: 'Snake' }, + { id: 8, name: 'Turtle' }, + { id: 9, name: 'Wombat' }, + ]} + > + {item => {item.name}} + +
+ Selected Value: {frequency} +
+ ); +}; + +// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test +// https://storybook.js.org/docs/react/workflows/unit-testing +export const itemsViaProps = ItemsViaProps.bind({}); diff --git a/stories/TextField.stories.tsx b/stories/TextField.stories.tsx new file mode 100644 index 00000000..b79c30fe --- /dev/null +++ b/stories/TextField.stories.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react'; +import { withDesign } from 'storybook-addon-designs'; +import { Form, TextField, TextFieldProps } from '../src'; +import InfoTip from './components/InfoTip'; + +const meta: Meta = { + title: 'TextField', + component: TextField, + decorators: [withDesign], + parameters: { + controls: { expanded: true }, + design: { + type: 'figma', + url: + 'https://www.figma.com/file/5mMInYH9JdJY389s8iBVQm/Component-Library?node-id=76%3A505', + }, + }, +}; + +export default meta; + +/** + * A gallery of all the variants + */ +export const Gallery = () => ( +
+ + + + + + The amount you will be charged + } + placeholder="enter your amount" + isRequired + validationState={'invalid'} + addonBefore="$" + errorMessage="This field is required" + /> + The amount you will be charged + } + placeholder="enter your amount" + isDisabled + addonBefore="$" + value="100" + /> + The amount you will be charged + } + placeholder="enter your amount" + isReadOnly + addonBefore="$" + value="100" + /> + +); + +const Template: Story = args => ( +
+ Label Text +
+); + +// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test +// https://storybook.js.org/docs/react/workflows/unit-testing +export const Default = Template.bind({}); diff --git a/stories/components/InfoTip.tsx b/stories/components/InfoTip.tsx index 500eddec..f2079575 100644 --- a/stories/components/InfoTip.tsx +++ b/stories/components/InfoTip.tsx @@ -50,6 +50,7 @@ function InfoTip({ children, postfix = true }: InfoTipProps) { background: 'transparent', cursor: 'pointer', color: 'inherit', + verticalAlign: 'bottom', }} > =2.76.0 <3.0.0" - node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -10528,16 +10111,6 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" - integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== - dependencies: - hosted-git-info "^4.0.1" - resolve "^1.20.0" - semver "^7.3.4" - validate-npm-package-license "^3.0.1" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -10567,7 +10140,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0, npm-run-path@^4.0.1: +npm-run-path@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -10732,7 +10305,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -10757,11 +10330,6 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -openurl@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/openurl/-/openurl-1.1.1.tgz#3875b4b0ef7a52c156f0db41d4609dbb0f94b387" - integrity sha1-OHW0sO96UsFW8NtB1GCduw+Us4c= - optimize-css-assets-webpack-plugin@^5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz#85883c6528aaa02e30bbad9908c92926bb52dc90" @@ -10862,13 +10430,6 @@ p-finally@^2.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-limit@3.1.0, p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -10883,6 +10444,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -11152,11 +10720,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" @@ -11212,7 +10775,7 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" -pkg-up@3.1.0, pkg-up@^3.1.0: +pkg-up@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== @@ -11226,11 +10789,6 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -11721,14 +11279,6 @@ progress-estimator@^0.2.2: humanize-duration "^3.15.3" log-update "^2.3.0" -progress-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-2.0.0.tgz#fac63a0b3d11deacbb0969abcc93b214bce19ed5" - integrity sha1-+sY6Cz0R3qy7CWmrzJOyFLzhntU= - dependencies: - speedometer "~1.0.0" - through2 "~2.0.3" - progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -11911,11 +11461,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-lru@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" - integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== - ramda@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" @@ -12108,6 +11653,11 @@ react-helmet-async@^1.0.2, react-helmet-async@^1.0.7: react-fast-compare "^3.2.0" shallowequal "^1.1.0" +react-hook-form@^7.27.1: + version "7.27.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.27.1.tgz#fe5fbcb6bf58751f66d9569e998d671480cc57f6" + integrity sha512-N3a7A6zIQ8DJeThisVZGtOUabTbJw+7DHJidmB9w8m3chckv2ZWKb5MHps9d2pPJqmCDoWe53Bos56bYmJms5w== + react-inspector@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" @@ -12356,14 +11906,6 @@ recursive-readdir@2.2.2: dependencies: minimatch "3.0.4" -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - refractor@^2.4.1: version "2.10.1" resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.1.tgz#166c32f114ed16fd96190ad21d5193d3afc7d34e" @@ -12571,7 +12113,7 @@ request-promise-native@^1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -"request@>=2.76.0 <3.0.0", request@^2.88.0: +request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12651,7 +12193,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12688,11 +12230,6 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -12807,7 +12344,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.6.0: +rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -13121,7 +12658,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -13191,11 +12728,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -13323,11 +12855,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== -speedometer@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.0.0.tgz#cd671cb06752c22bca3370e2f334440be4fc62e2" - integrity sha1-zWccsGdSwivKM3Di8zREC+T8YuI= - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -13654,11 +13181,6 @@ stylis@^4.0.3: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg== -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -13707,11 +13229,6 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -13858,7 +13375,7 @@ throttle-debounce@^3.0.1: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== -through2@^2.0.0, through2@~2.0.3: +through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -13896,13 +13413,6 @@ tiny-glob@^0.2.6: globalyzer "0.1.0" globrex "^0.1.2" -tmp-promise@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.2.tgz#6e933782abff8b00c3119d63589ca1fb9caaa62a" - integrity sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA== - dependencies: - tmp "^0.2.0" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -13910,13 +13420,6 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -14003,16 +13506,6 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -trim-newlines@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" - integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== - trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" @@ -14028,7 +13521,7 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -ts-dedent@^1.0.0, ts-dedent@^1.1.0: +ts-dedent@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-1.2.0.tgz#6aa2229d837159bb6d635b6b233002423b91e0b0" integrity sha512-6zSJp23uQI+Txyz5LlXMXAHpUhY4Hi0oluXny0OgIR7g/Cromq4vDBnhtbBdyIV34g0pgwxUvnvg+jLJe4c1NA== @@ -14175,11 +13668,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -14197,11 +13685,6 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" - integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== - type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -14407,11 +13890,6 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -14568,11 +14046,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -14919,15 +14392,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -14960,11 +14424,6 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xmlbuilder@^10.0.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" - integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== - xmlchars@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -14980,11 +14439,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -15008,24 +14462,6 @@ yargs-parser@18.x, yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -15043,14 +14479,6 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yarn-or-npm@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz#6336eea4dff7e23e226acc98c1a8ada17a1b8666" - integrity sha512-fTiQP6WbDAh5QZAVdbMQkecZoahnbOjClTQhzv74WX5h2Uaidj1isf9FDes11TKtsZ0/ZVfZsqZ+O3x6aLERHQ== - dependencies: - cross-spawn "^6.0.5" - pkg-dir "^4.2.0" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"