Browse Source

Add MenuSelect, improve option demo page

The MenuSelect functions to have selected options in a list view.

The option demo page now also uses correct components.
master
TheoryOfNekomata 3 years ago
parent
commit
7bb7160d9c
11 changed files with 1472 additions and 866 deletions
  1. +69
    -0
      docs/todo.md
  2. +16
    -6
      src/modules/base-checkcontrol/index.ts
  3. +5
    -0
      src/modules/base-selectcontrol/index.ts
  4. +1
    -1
      src/modules/base-textcontrol/index.ts
  5. +3
    -8
      src/modules/option/components/DropdownSelect/index.tsx
  6. +196
    -0
      src/modules/option/components/MenuSelect/index.tsx
  7. +17
    -5
      src/modules/option/components/ToggleSwitch/index.tsx
  8. +1
    -0
      src/modules/option/index.ts
  9. +1
    -0
      src/modules/option/web-option-react.test.ts
  10. +1162
    -846
      src/pages/categories/option/index.tsx
  11. +1
    -0
      tsconfig.json

+ 69
- 0
docs/todo.md View File

@@ -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`

+ 16
- 6
src/modules/base-checkcontrol/index.ts View File

@@ -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;
`
),
),
);



+ 5
- 0
src/modules/base-selectcontrol/index.ts View File

@@ -0,0 +1,5 @@
export interface SelectOption {
label: string,
value?: string | number | readonly string[]
children?: SelectOption[]
}

+ 1
- 1
src/modules/base-textcontrol/index.ts View File

@@ -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;


+ 3
- 8
src/modules/option/components/DropdownSelect/index.tsx View File

@@ -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<React.HTMLProps<HTMLSelectElement>, 'size
/**
* Options available for the component's values.
*/
options?: SelectOption[],
options?: SelectControlBase.SelectOption[],
}

/**


+ 196
- 0
src/modules/option/components/MenuSelect/index.tsx View File

@@ -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<RenderOptionsProps> = ({
options,
optionComponent: Option = 'option',
optgroupComponent: Optgroup = 'optgroup',
level = 0,
}: RenderOptionsProps) => (
<>
{
options.map((o) => {
if (typeof o.value !== 'undefined') {
return (
<Option
key={`${o.label}:${o.value.toString()}`}
value={o.value}
>
{o.label}
</Option>
);
}

if (typeof o.children !== 'undefined') {
if (level === 0) {
return (
<Optgroup
key={o.label}
label={o.label}
>
<RenderOptions
options={o.children}
optionComponent={Option}
optgroupComponent={Optgroup}
level={level + 1}
/>
</Optgroup>
);
}
return (
<React.Fragment
key={o.label}
>
<Option
disabled
>
{o.label}
</Option>
<RenderOptions
options={o.children}
optionComponent={Option}
optgroupComponent={Optgroup}
level={level + 1}
/>
</React.Fragment>
);
}

return null;
})
}
</>
);

export type MenuSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, '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<HTMLSelectElement, MenuSelectProps>(({
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<TextControlBase.TextControlBaseArgs>(() => ({
block,
border,
size,
indicator: true,
style,
resizable: true,
predefinedValues: true,
}), [block, border, size, style]);

return (
<div
className={TextControlBase.Root(styleArgs)}
>
<select
{...etcProps}
className={TextControlBase.Input(styleArgs)}
ref={ref}
aria-label={label}
style={{
height: TextControlBase.MIN_HEIGHTS[size],
}}
size={2}
data-testid="input"
>
<RenderOptions
options={options}
/>
</select>
{
border && (
<span
data-testid="border"
/>
)
}
{
label && !hiddenLabel && (
<div
data-testid="label"
className={TextControlBase.LabelWrapper(styleArgs)}
>
{label}
</div>
)
}
{hint && (
<div
className={TextControlBase.HintWrapper(styleArgs)}
data-testid="hint"
>
<div
className={TextControlBase.Hint()}
>
{hint}
</div>
</div>
)}
{indicator && (
<div
className={TextControlBase.IndicatorWrapper(styleArgs)}
>
{indicator}
</div>
)}
</div>
);
});

MenuSelect.displayName = 'MenuSelect';

+ 17
- 5
src/modules/option/components/ToggleSwitch/index.tsx View File

@@ -2,6 +2,14 @@ import * as React from 'react';
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol';

export type ToggleSwitchProps = Omit<React.HTMLProps<HTMLInputElement>, '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<React.HTMLProps<HTMLInputElement>, '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<React.HTMLProps<HTMLInputElement>, 'size' |
export const ToggleSwitch = React.forwardRef<HTMLInputElement, ToggleSwitchProps>(
(
{
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<HTMLInputElement, ToggleSwitchProps
menuItem: false,
appearance: CheckControlBase.CheckControlAppearance.SWITCH,
type: 'checkbox',
}), [block, compact]);
uncheckedLabel: Boolean(uncheckedLabel),
}), [block, compact, uncheckedLabel]);

return (
<div
@@ -57,7 +67,9 @@ export const ToggleSwitch = React.forwardRef<HTMLInputElement, ToggleSwitchProps
className={CheckControlBase.CheckStateContainer(styleProps)}
/>
<span>
<span />
<span>
{uncheckedLabel}
</span>
<span
className={CheckControlBase.CheckIndicatorArea(styleProps)}
>
@@ -85,7 +97,7 @@ export const ToggleSwitch = React.forwardRef<HTMLInputElement, ToggleSwitchProps
</span>
</span>
<span>
{children}
{checkedLabel}
{
subtext
&& (


+ 1
- 0
src/modules/option/index.ts View File

@@ -1,4 +1,5 @@
export * from './components/DropdownSelect';
export * from './components/MenuSelect';
export * from './components/RadioButton';
export * from './components/RadioTickBox';
export * from './components/ToggleButton';


+ 1
- 0
src/modules/option/web-option-react.test.ts View File

@@ -3,6 +3,7 @@ import * as WebOptionReact from '.';
describe('web-option-react', () => {
it.each([
'DropdownSelect',
'MenuSelect',
'RadioButton',
'RadioTickBox',
'ToggleButton',


+ 1162
- 846
src/pages/categories/option/index.tsx
File diff suppressed because it is too large
View File


+ 1
- 0
tsconfig.json View File

@@ -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"]
}


Loading…
Cancel
Save