diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 0000000..fe8ef25 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,69 @@ +# Action + +- [X] `ActionButton` + +# Color + +- [ ] `ColorSlider` +- [ ] `Palette` +- [ ] `Swatch` + +# File + +- [ ] `FileArea` +- [ ] `FilePicker` + +# Formatted + +- [ ] `EmailInput` +- [ ] `TelInput` +- [ ] `UrlInput` + +# Freeform + +- [X] `MaskedTextInput` +- [X] `MultilineTextInput` +- [X] `TextInput` + +# Geo + +- [ ] `Map` + +# Information + +- [X] `Badge` + +# Navigation + +- [ ] `Breadcrumb` +- [X] `LinkButton` +- [ ] `Stepper` +- [ ] `TabPanel` + +# Numeric + +- [ ] `Knob` +- [ ] `Range2D` +- [ ] `Slider` +- [ ] `Spinner` + +# Option + +- [X] `DropdownSelect` +- [ ] `MenuSelect` +- [X] `RadioButton` +- [X] `RadioTickBox` +- [ ] `TagInput` +- [X] `ToggleButton` +- [X] `ToggleSwitch` +- [X] `ToggleTickBox` + +# Temporal + +- [ ] `Calendar` +- [ ] `Clock` +- [ ] `DateDropdown` +- [ ] `DurationInput` +- [ ] `MonthDaySpinner` +- [ ] `TimeSpinner` +- [ ] `YearMonthSpinner` diff --git a/src/modules/base-checkcontrol/index.ts b/src/modules/base-checkcontrol/index.ts index 58337ce..a7ede25 100644 --- a/src/modules/base-checkcontrol/index.ts +++ b/src/modules/base-checkcontrol/index.ts @@ -11,6 +11,7 @@ export type CheckControlBaseArgs = { appearance: CheckControlAppearance, block: boolean, type: string, + uncheckedLabel: boolean, } export const CheckStateContainer = ({ @@ -154,6 +155,7 @@ export const CheckIndicatorArea = ({ compact, appearance, type, + uncheckedLabel, }: CheckControlBaseArgs): string => css.cx( css` display: inline-grid; @@ -219,7 +221,12 @@ export const CheckIndicatorArea = ({ width: 2.5em; height: 1.5em; border-radius: 0.75em; - ` + `, + css.if(uncheckedLabel) ( + css.dynamic({ + 'margin-left': compact ? '0.375rem' : '0.75rem', + }) + ), ), css.nest('& + *') ( css.dynamic({ @@ -256,7 +263,7 @@ export const CheckIndicatorWrapper = ({ transition-property: margin-left, margin-right; transition-duration: 150ms; transition-timing-function: ease-out; - ` + `, ), ); @@ -282,6 +289,7 @@ export const CheckIndicator = ({ export const ClickAreaWrapper = ({ block, appearance, + uncheckedLabel, }: CheckControlBaseArgs) => css.cx( css` vertical-align: middle; @@ -296,10 +304,12 @@ export const ClickAreaWrapper = ({ ` ), css.if (appearance === CheckControlAppearance.SWITCH) ( - css` - padding-left: 3.25rem; - text-indent: -3.25rem; - ` + css.if (!uncheckedLabel) ( + css` + padding-left: 3.25rem; + text-indent: -3.25rem; + ` + ), ), ); diff --git a/src/modules/base-selectcontrol/index.ts b/src/modules/base-selectcontrol/index.ts new file mode 100644 index 0000000..1b44f45 --- /dev/null +++ b/src/modules/base-selectcontrol/index.ts @@ -0,0 +1,5 @@ +export interface SelectOption { + label: string, + value?: string | number | readonly string[] + children?: SelectOption[] +} diff --git a/src/modules/base-textcontrol/index.ts b/src/modules/base-textcontrol/index.ts index 0be70a5..9805ea4 100644 --- a/src/modules/base-textcontrol/index.ts +++ b/src/modules/base-textcontrol/index.ts @@ -121,7 +121,7 @@ export const LabelWrapper = ({ position: absolute; top: 0; left: 0; - max-width: 100%; + width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/src/modules/option/components/DropdownSelect/index.tsx b/src/modules/option/components/DropdownSelect/index.tsx index a547ab3..2eb5c87 100644 --- a/src/modules/option/components/DropdownSelect/index.tsx +++ b/src/modules/option/components/DropdownSelect/index.tsx @@ -1,14 +1,9 @@ import * as React from 'react'; import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; - -export interface SelectOption { - label: string, - value?: string | number | readonly string[] - children?: SelectOption[] -} +import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol'; type RenderOptionsProps = { - options: SelectOption[], + options: SelectControlBase.SelectOption[], optionComponent?: React.ElementType, optgroupComponent?: React.ElementType, level?: number, @@ -107,7 +102,7 @@ export type DropdownSelectProps = Omit, 'size /** * Options available for the component's values. */ - options?: SelectOption[], + options?: SelectControlBase.SelectOption[], } /** diff --git a/src/modules/option/components/MenuSelect/index.tsx b/src/modules/option/components/MenuSelect/index.tsx new file mode 100644 index 0000000..9f22377 --- /dev/null +++ b/src/modules/option/components/MenuSelect/index.tsx @@ -0,0 +1,196 @@ +import * as React from 'react'; +import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; +import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol'; + +type RenderOptionsProps = { + options: SelectControlBase.SelectOption[], + optionComponent?: React.ElementType, + optgroupComponent?: React.ElementType, + level?: number, +} + +const RenderOptions: React.VFC = ({ + options, + optionComponent: Option = 'option', + optgroupComponent: Optgroup = 'optgroup', + level = 0, +}: RenderOptionsProps) => ( + <> + { + options.map((o) => { + if (typeof o.value !== 'undefined') { + return ( + + ); + } + + if (typeof o.children !== 'undefined') { + if (level === 0) { + return ( + + + + ); + } + return ( + + + + + ); + } + + return null; + }) + } + +); + +export type MenuSelectProps = Omit, 'size' | 'style'> & { + /** + * Short textual description indicating the nature of the component's value. + */ + label?: React.ReactNode, + /** + * Short textual description as guidelines for valid input values. + */ + hint?: React.ReactNode, + /** + * Size of the component. + */ + size?: TextControlBase.TextControlSize, + /** + * Additional description, usually graphical, indicating the nature of the component's value. + */ + indicator?: React.ReactNode, + /** + * Should the component display a border? + */ + border?: boolean, + /** + * Should the component occupy the whole width of its parent? + */ + block?: boolean, + /** + * Style of the component. + */ + style?: TextControlBase.TextControlStyle, + /** + * Is the label hidden? + */ + hiddenLabel?: boolean, + /** + * Options available for the component's values. + */ + options?: SelectControlBase.SelectOption[], +} + +export const MenuSelect = React.forwardRef(({ + label = '', + hint = '', + indicator = null, + size = TextControlBase.TextControlSize.MEDIUM, + border = false, + block = false, + style = TextControlBase.TextControlStyle.DEFAULT, + hiddenLabel = false, + options = [], + className: _className, + placeholder: _placeholder, + as: _as, + ...etcProps +}: MenuSelectProps, ref) => { + const styleArgs = React.useMemo(() => ({ + block, + border, + size, + indicator: true, + style, + resizable: true, + predefinedValues: true, + }), [block, border, size, style]); + + return ( +
+ + { + border && ( + + ) + } + { + label && !hiddenLabel && ( +
+ {label} +
+ ) + } + {hint && ( +
+
+ {hint} +
+
+ )} + {indicator && ( +
+ {indicator} +
+ )} +
+ ); +}); + +MenuSelect.displayName = 'MenuSelect'; diff --git a/src/modules/option/components/ToggleSwitch/index.tsx b/src/modules/option/components/ToggleSwitch/index.tsx index 0daa55e..f57a52a 100644 --- a/src/modules/option/components/ToggleSwitch/index.tsx +++ b/src/modules/option/components/ToggleSwitch/index.tsx @@ -2,6 +2,14 @@ import * as React from 'react'; import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; export type ToggleSwitchProps = Omit, 'size' | 'type' | 'style'> & { + /** + * Label of the component when in the unchecked state. + */ + uncheckedLabel?: React.ReactNode, + /** + * Label of the component when in the checked state. + */ + checkedLabel?: React.ReactNode, /** * Should the component occupy the whole width of its parent? */ @@ -14,7 +22,6 @@ export type ToggleSwitchProps = Omit, 'size' | * Complementary content of the component. */ subtext?: React.ReactNode, - // TODO - opposite children - label used for "off" value } /** @@ -25,12 +32,14 @@ export type ToggleSwitchProps = Omit, 'size' | export const ToggleSwitch = React.forwardRef( ( { - children, + checkedLabel, + uncheckedLabel, block = false, compact = false, subtext, className: _className, as: _as, + children: _children, ...etcProps }: ToggleSwitchProps, ref, @@ -41,7 +50,8 @@ export const ToggleSwitch = React.forwardRef - + + {uncheckedLabel} + @@ -85,7 +97,7 @@ export const ToggleSwitch = React.forwardRef - {children} + {checkedLabel} { subtext && ( diff --git a/src/modules/option/index.ts b/src/modules/option/index.ts index 27c507f..9082200 100644 --- a/src/modules/option/index.ts +++ b/src/modules/option/index.ts @@ -1,4 +1,5 @@ export * from './components/DropdownSelect'; +export * from './components/MenuSelect'; export * from './components/RadioButton'; export * from './components/RadioTickBox'; export * from './components/ToggleButton'; diff --git a/src/modules/option/web-option-react.test.ts b/src/modules/option/web-option-react.test.ts index c4a2ecc..2d851b7 100644 --- a/src/modules/option/web-option-react.test.ts +++ b/src/modules/option/web-option-react.test.ts @@ -3,6 +3,7 @@ import * as WebOptionReact from '.'; describe('web-option-react', () => { it.each([ 'DropdownSelect', + 'MenuSelect', 'RadioButton', 'RadioTickBox', 'ToggleButton', diff --git a/src/pages/categories/option/index.tsx b/src/pages/categories/option/index.tsx index 7e369e4..8295f4e 100644 --- a/src/pages/categories/option/index.tsx +++ b/src/pages/categories/option/index.tsx @@ -1,11 +1,12 @@ import { NextPage } from 'next'; import { TextControlSize, TextControlStyle } from '@tesseract-design/web-base-textcontrol'; import * as Option from '@tesseract-design/web-option-react'; -import { CheckControlAppearance } from '@tesseract-design/web-base-checkcontrol'; +import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol'; import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button'; +import { DefaultLayout } from 'src/components/DefaultLayout'; type Props = { - options: Option.SelectOption[], + options: SelectControlBase.SelectOption[], } const OptionPage: NextPage = ({ @@ -45,879 +46,1194 @@ const OptionPage: NextPage = ({ { label: 'Coffee', value: 'coffee', - } + }, ], }, ], }) => { return ( -
-
-
-

- Option -

-
-
-

- DropdownSelect -

-
-
-

- Default -

-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
+ +
+
+
+

+ DropdownSelect +

+
+
+

+ Default +

+
+
+
+ +
+
+ +
+
+
-
-
- Disabled Test -
-
- -
-
- -
-
- -
-
- -
-
- -
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ Disabled Test +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-
- -
-
- Hi, my name is - {' '} - - {' '} - but you can call me - {' '} - - .
-
+
+ +
+
+ Hi, my name is + {' '} + + {' '} + but you can call me + {' '} + + . +
+
+
+
+
+

Alternate

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
-
-

Alternate

-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
+
+
+
+
+
+
+
+

+ MenuSelect +

+
+
+

+ Default +

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-
-
-
-
-

- RadioButton -

-
-
-

Default

-
-
-
- - RadioButton - -
+
+
+
+

Alternate

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-
-
-

Button

-
-
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
+
+
+
+

+ Multiple +

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-
-
-

Sizes

-
-
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
+
+
+
+
+
+
+
+

+ RadioButton +

+
+
+

Variants

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button +
-
-
-

Compact

-
-
-
- - Button - -
-
- - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
+
+
+
+

Sizes

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+
+
+
+

Compact

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button +
-
+
+
+
+
+
+
+
+

+ RadioTickBox +

+
+
+
+ + RadioButton + +
-
-
-

- Checkbox -

-
-
-

Default

-
-
-
- - Checkbox - -
-
- - Checkbox - -
+
+
+
+
+
+

+ ToggleButton +

+
+
+

Variants

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button +
-
-
-

Switch

-
-
-
- - Checkbox - -
-
- - Checkbox - -
+
+
+
+

Sizes

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button + +
+
+ + Button +
-
-
-

Button

-
-
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
+
+
+
+

Compact

+
+
+
+ + Button + +
+
+ + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button + +
+
+ + Subtext + + } + > + Button +
-
-
-

Sizes

-
-
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
-
- - Button - -
+
+
+
+
+
+
+
+

+ ToggleSwitch +

+
+
+

Default

+
+
+
+ +
+
+
-
-
-

Compact

-
-
-
- - Button - -
-
- - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
-
- - Subtext - - } - > - Button - -
+
+
+
+

With Unchecked Label

+
+
+
+ +
+
+
-
-
-
-
-

- TagList -

-
- TODO -
-
-
-

- ComboBox -

-
- TODO input with datalist -
-
+
+
+ - - -
- ) -} + +
+
+

+ ToggleTickBox +

+
+
+

Default

+
+
+
+ + Checkbox + +
+
+ + Checkbox + +
+
+
+
+
+
+
+
+
+

+ TagList +

+
+ TODO +
+
+
+
+
+

+ ComboBox +

+
+ TODO input with datalist +
+
+
+ + + ); +}; -export default OptionPage +export default OptionPage; diff --git a/tsconfig.json b/tsconfig.json index 9fc649e..f08b6ef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "@tesseract-design/web-base-badge": ["./src/modules/base-badge"], "@tesseract-design/web-base-button": ["./src/modules/base-button"], "@tesseract-design/web-base-checkcontrol": ["./src/modules/base-checkcontrol"], + "@tesseract-design/web-base-selectcontrol": ["./src/modules/base-selectcontrol"], "@tesseract-design/web-base-textcontrol": ["./src/modules/base-textcontrol"], "@tesseract-design/css-utils": ["./src/modules/css-utils"] }