1
Fork 0
mirror of https://github.com/Steffo99/bluelib.git synced 2024-12-23 03:54:21 +00:00

Implement (properly) inputs and forms (#1)

* 🔧 Use `<label>` in the `ThreeRadios` and `ThreeCheckboxes` stories
* 🔧 Make `onChange` return void, as React does not support implicit preventDefault  
  https://reactjs.org/docs/handling-events.html
* 🚧 Some work on forms
* 💥 A huge non-atomic commit
* 💥 Another huge non-atomic commit
* 💥 The final non-atomic commit
* 💥 Just kidding, have another
* 🚧 A bit more
* 🚧 A bot more
*  Add `onSimpleChange` events
* 🚧 More work on inputs
*  Finish `FormRadioGroup`
*  Finish `FormCheckboxGroup`
*  Finish `Form`
* 💥 idk anymore
*  Add `Button` input
*  Add `FormRow` to forms
* 🔨 Fix storybook preview.js
* 🔧 Prevent button from submitting a form
* 📔 Fix a bit the Form story
* 💥 Tweak forms a bit more
This commit is contained in:
Steffo 2021-08-24 04:22:15 +02:00 committed by GitHub
parent 6355ddee36
commit 8b7c57aae6
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
92 changed files with 1644 additions and 753 deletions

View file

@ -2,7 +2,43 @@ import { Bluelib } from "../src/components/Bluelib"
export const parameters = { export const parameters = {
actions: { argTypes: {
argTypesRegex: "^on[A-Z][a-z]*$" bluelibClassNames: {
control: {type: "string"},
description: "Additional Bluelib classNames to be appended to the element's classNames",
table: {category: "Global props"}
},
customColor: {
control: {type: "color"},
description: "Apply a Bluelib custom color to the element",
table: {category: "Global props"}
},
disabled: {
control: {type: "boolean"},
description: "Apply the disabled status to an element",
table: {category: "Global props"}
}
},
options: {
storySort: {
order: [
"Core",
"Layouts",
"Panels",
"Chapters",
"Separators",
"Images",
"Tables",
"Lists",
"Status",
"Inputs",
"Forms",
"Common",
"Annotations",
"Semantics",
"Colors",
"Internals",
]
}
}, },
} }

View file

@ -13,12 +13,14 @@
"@types/node": "^12.0.0", "@types/node": "^12.0.0",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@types/uuid": "^8.3.1",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"color": "https://github.com/Steffo99/color", "color": "https://github.com/Steffo99/color",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"typescript": "^4.1.2", "typescript": "^4.1.2",
"uuid": "^8.3.2",
"web-vitals": "^1.0.1" "web-vitals": "^1.0.1"
}, },
"devDependencies": { "devDependencies": {

@ -1 +1 @@
Subproject commit 0fe4c9e477a681031c4d62fd9a7c39e94befa849 Subproject commit 48722dcbfa0d6e9e1e5fe3bcb84b0be102d5db38

View file

@ -1,42 +1,23 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../utils/Decorators" import * as Decorators from "../utils/Decorators"
import { BaseElement } from "./BaseElement" import { BaseElement as BaseElementComponent } from "./BaseElement"
import { Bluelib } from "./Bluelib" import { Bluelib } from "./Bluelib"
export default { export default {
component: BaseElement, component: BaseElementComponent,
title: "Bluelib/BaseElement", title: "Internals/Base Element",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: {
customColor: {
control: {type: "color"},
},
},
} }
export const Default = props => ( export const BaseElement = props => (
<BaseElement kind={"div"} {...props}> <BaseElementComponent kind={"div"} {...props}>
This is a text node child. This is a text node child.
</BaseElement> </BaseElementComponent>
) )
Default.args = { BaseElement.args = {
kind: "div", kind: "div",
disabled: false, disabled: false,
} }
export const CustomColor = Default.bind({})
CustomColor.args = {
...Default.args,
customColor: "#ff7f00",
}
export const Disabled = Default.bind({})
Disabled.args = {
...Default.args,
disabled: true,
}

View file

@ -7,29 +7,28 @@ import Color from "color"
import mergeClassNames, {Argument as ClassNamesArgument} from "classnames" import mergeClassNames, {Argument as ClassNamesArgument} from "classnames"
export interface BaseElementProps { export interface BaseElementProps extends React.HTMLProps<any> {
kind: Types.ComponentKind, kind: string,
bluelibClassNames?: Types.ClassNames, bluelibClassNames?: Types.ClassNames,
customColor?: typeof Color,
disabled?: boolean, disabled?: boolean,
customColor?: typeof Color,
[props: string]: any,
} }
export function BaseElement({kind, bluelibClassNames, customColor, ...props}: BaseElementProps): JSX.Element { export function BaseElement({kind = "div", bluelibClassNames, disabled = false, customColor, ...props}: BaseElementProps): JSX.Element {
// Set the Bluelib color // Set the Bluelib color
if(customColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("color", customColor)} if(customColor) {
props.style = {...props.style, ...Colors.colorToBluelibStyle("color", customColor)}
}
// Possibly disable the element // Possibly disable the element
if(props.disabled) bluelibClassNames = mergeClassNames(bluelibClassNames, "status-disabled") bluelibClassNames = mergeClassNames(bluelibClassNames, disabled ? "status-disabled" : "")
// @ts-ignore
props.disabled = disabled
// Map regular class names to module class names // Map regular class names to module class names
bluelibClassNames = BluelibMapper.rootToModule(bluelibClassNames) bluelibClassNames = BluelibMapper.rootToModule(bluelibClassNames)
props.className = mergeClassNames(props.className, bluelibClassNames) props.className = mergeClassNames(props.className, bluelibClassNames)
// Dynamically determine the element kind return React.createElement(kind, props)
const Kind = kind
return <Kind {...props}/>
} }

View file

@ -7,7 +7,7 @@ import Color from "color"
export default { export default {
component: Bluelib, component: Bluelib,
title: "Bluelib/Bluelib", title: "Core/Bluelib",
decorators: [Decorators.Fill], decorators: [Decorators.Fill],
parameters: { parameters: {
layout: "fullscreen", layout: "fullscreen",

View file

@ -19,7 +19,7 @@ const BuiltinThemes = {
} }
export interface BluelibProps { export interface BluelibProps extends Types.BluelibHTMLProps<HTMLDivElement> {
theme: "paper" | "royalblue" | "hacker" | "sophon", theme: "paper" | "royalblue" | "hacker" | "sophon",
backgroundColor?: typeof Color, backgroundColor?: typeof Color,
@ -38,8 +38,6 @@ export interface BluelibProps {
magentaColor?: typeof Color, magentaColor?: typeof Color,
grayColor?: typeof Color, grayColor?: typeof Color,
polarity?: number, polarity?: number,
[props: string]: any,
} }
@ -81,7 +79,10 @@ export function Bluelib({
if(blueColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("blue", blueColor)} if(blueColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("blue", blueColor)}
if(magentaColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("magenta", magentaColor)} if(magentaColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("magenta", magentaColor)}
if(grayColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("gray", grayColor)} if(grayColor) props.style = {...props.style, ...Colors.colorToBluelibStyle("gray", grayColor)}
if(polarity) props.style["--bluelib-polarity"] = polarity if(polarity) {
// @ts-ignore
props.style["--bluelib-polarity"] = polarity
}
return ( return (
<BaseElement <BaseElement

View file

@ -20,16 +20,14 @@ export default {
} }
export const Default = props => ( export const Basic = props => (
<Chapter {...props}> <Chapter {...props}>
<Box>First</Box> <Box>First</Box>
<Box>Second</Box> <Box>Second</Box>
<Box>Third</Box> <Box>Third</Box>
</Chapter> </Chapter>
) )
Default.args = { Basic.args = {}
disabled: false,
}
export const AutoWrap = props => ( export const AutoWrap = props => (
@ -46,14 +44,18 @@ export const AutoWrap = props => (
<Box>Tenth</Box> <Box>Tenth</Box>
<Box>Eleventh</Box> <Box>Eleventh</Box>
<Box>Twelfth</Box> <Box>Twelfth</Box>
<Box>Thirtheenth</Box> <Box>Thirteenth</Box>
<Box>Fourtheenth</Box> <Box>Fourteenth</Box>
<Box>Fiftheenth</Box> <Box>Fifteenth</Box>
<Box>Sixteenth</Box>
<Box>Seventeenth</Box>
<Box>Eighteenth</Box>
<Box>Ninteenth</Box>
<Box>Ninteenth</Box>
<Box>Twentieth</Box>
</Chapter> </Chapter>
) )
AutoWrap.args = { AutoWrap.args = {}
disabled: false,
}
export const ForceWrap = props => ( export const ForceWrap = props => (
@ -65,6 +67,4 @@ export const ForceWrap = props => (
<Box>Fourth</Box> <Box>Fourth</Box>
</Chapter> </Chapter>
) )
ForceWrap.args = { ForceWrap.args = {}
disabled: false,
}

View file

@ -3,11 +3,10 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {ChapterForceWrap} from "./ChapterForceWrap";
interface ChapterProps { export interface ChapterProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
[props: string]: any,
}
export function Chapter({...props}: ChapterProps): JSX.Element { export function Chapter({...props}: ChapterProps): JSX.Element {
@ -19,15 +18,4 @@ export function Chapter({...props}: ChapterProps): JSX.Element {
} }
interface ChapterForceWrapProps { Chapter.ForceWrap = ChapterForceWrap
[props: string]: any,
}
Chapter.ForceWrap = function({...props}: ChapterForceWrapProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "chapter-forcewrap")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface ChapterForceWrapProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function ChapterForceWrap({...props}: ChapterForceWrapProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "chapter-forcewrap")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,74 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { Form as FormComponent } from "./Form"
import { FormField } from "./FormField"
import { FormArea } from "./FormArea"
import { FormSelect } from "./FormSelect"
import { Option } from "../inputs/Option"
import { FormMultiselect } from "./FormMultiselect"
import { FormRadioGroup } from "./FormRadioGroup"
import { FormCheckboxGroup } from "./FormCheckboxGroup"
import { FormRow } from "./FormRow"
import { Button } from "../inputs/Button"
import { Parenthesis } from "../panels/Parenthesis"
export default {
component: FormComponent,
subcomponents: {FormField, FormRow, FormArea, FormSelect, FormRadioGroup, FormCheckboxGroup, Parenthesis, Option, Button},
title: "Forms/Form",
decorators: [Decorators.Box, Decorators.Bluelib],
}
export const Form = props => (
<FormComponent {...props}>
<FormComponent.Field label={"Username"}/>
<FormComponent.Field label={"Password"} type={"password"}/>
<FormComponent.Row>
<Parenthesis>Enter the details of your characters below.</Parenthesis>
</FormComponent.Row>
<FormComponent.Field label={"Name"}/>
<FormComponent.Area label={"Backstory"}/>
<FormComponent.Select label={"Gender"}>
<FormComponent.Select.Option value={"Male"}/>
<FormComponent.Select.Option value={"Female"}/>
<FormComponent.Select.Option value={"Non-binary"}/>
</FormComponent.Select>
<FormComponent.Field label={"Level"} type={"number"} min={1} max={20}/>
<FormComponent.Radios label={"Alignment"} row={true} options={[
"Lawful good",
"Lawful neutral",
"Lawful evil",
"Neutral good",
"Neutral",
"Neutral evil",
"Chaotic good",
"Chaotic neutral",
"Chaotic evil",
"Other",
]}/>
<FormComponent.Checkboxes label={"Classes"} row={false} options={[
"Artificer",
"Barbarian",
"Bard",
"Cleric",
"Druid",
"Fighter",
"Monk",
"Paladin",
"Ranger",
"Rogue",
"Sorcerer",
"Warlock",
"Wizard",
]}/>
<FormComponent.Row>
<FormComponent.Button>Throw fireball</FormComponent.Button>
<FormComponent.Button>Shoot a magic missile</FormComponent.Button>
<FormComponent.Button>Save character</FormComponent.Button>
</FormComponent.Row>
</FormComponent>
)
Form.args = {}

View file

@ -0,0 +1,44 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormArea} from "./FormArea";
import {FormCheckboxGroup} from "./FormCheckboxGroup";
import {FormField} from "./FormField";
import {FormMultiselect} from "./FormMultiselect";
import {FormRadioGroup} from "./FormRadioGroup";
import {FormRow} from "./FormRow";
import {FormSelect} from "./FormSelect";
import {FormLabel} from "./FormLabel";
import {FormPair} from "./FormPair";
import {FormGroup} from "./FormGroup";
import {Button} from "../inputs/Button";
export interface FormProps extends Types.BluelibHTMLProps<HTMLFormElement> {}
export function Form({...props}: FormProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "form")
return (
<BaseElement kind={"form"} {...props}/>
)
}
Form.Area = FormArea
Form.Checkboxes = FormCheckboxGroup
Form.Field = FormField
Form.Multiselect = FormMultiselect
Form.Radios = FormRadioGroup
Form.Row = FormRow
Form.Select = FormSelect
Form.Button = Button
Form.Internals = {
Label: FormLabel,
Pair: FormPair,
Group: FormGroup,
}

View file

@ -0,0 +1,23 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormArea as FormAreaComponent } from "./FormArea"
export default {
component: FormAreaComponent,
title: "Forms/Form Area",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const FormArea = props => (
<FormAreaComponent {...props}/>
)
FormArea.args = {
label: "Bio",
}

View file

@ -0,0 +1,30 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {Area, AreaProps} from "../inputs/Area";
export interface FormAreaProps extends AreaProps {
label: string,
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
}
export function FormArea({label, validity, pairProps, labelProps, ...props}: FormAreaProps): JSX.Element {
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={<Area {...props}/>}
validity={validity}
{...pairProps}
/>
)
}

View file

@ -0,0 +1,34 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormCheckboxGroup as FormCheckboxGroupComponent } from "./FormCheckboxGroup"
export default {
component: FormCheckboxGroupComponent,
title: "Forms/Form Checkbox Group",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const Unmanaged = props => (
<FormCheckboxGroupComponent {...props}/>
)
Unmanaged.args = {
label: "Sizes",
options: ["XS", "S", "M", "L", "XL"],
value: undefined,
row: false,
disabled: false,
}
export const Managed = Unmanaged.bind({})
Managed.args = {
...Unmanaged.args,
value: ["M"],
}

View file

@ -0,0 +1,83 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import * as UUID from "uuid"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {FormGroup, FormGroupProps} from "./FormGroup";
import {LabelledCheckbox, LabelledCheckboxProps} from "../inputs/LabelledCheckbox";
export interface FormCheckboxGroupProps extends Types.BluelibProps {
name?: string,
label: string,
options: string[],
row?: boolean,
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
onSimpleChange?: (value: string[]) => void,
value?: string[],
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
groupProps?: FormGroupProps,
checkboxProps?: LabelledCheckboxProps,
}
export function FormCheckboxGroup({name, label, options, row, onChange, onSimpleChange, value, validity, pairProps, labelProps, groupProps, checkboxProps, disabled, bluelibClassNames, customColor}: FormCheckboxGroupProps): JSX.Element {
if(!name) {
name = UUID.v4()
}
const checkboxes = options.map<JSX.Element>(option => (
<LabelledCheckbox
label={option}
value={option}
row={row}
checked={value ? value.includes(option) : undefined}
name={name}
disabled={disabled}
{...checkboxProps}
/>
))
const onChangeWrapped = React.useCallback(
event => {
if(onChange) onChange(event)
if(value && onSimpleChange) {
if(event.target.checked) {
onSimpleChange(value.concat(event.target.value))
}
else {
const valueCopy = Array.from(value)
const indexOf = valueCopy.indexOf(event.target.value)
if(indexOf !== -1) valueCopy.splice(indexOf, 1)
onSimpleChange(valueCopy)
}
}
},
[onChange, value, onSimpleChange]
)
const group = (
<FormGroup onChange={onChangeWrapped} value={value} {...groupProps}>
{checkboxes}
</FormGroup>
)
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={group}
validity={validity}
bluelibClassNames={bluelibClassNames}
customColor={customColor}
{...pairProps}
/>
)
}

View file

@ -0,0 +1,23 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormField as FormFieldComponent } from "./FormField"
export default {
component: FormFieldComponent,
title: "Forms/Form Field",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const FormField = props => (
<FormFieldComponent {...props}/>
)
FormField.args = {
label: "Username",
}

View file

@ -0,0 +1,30 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {Field, FieldProps} from "../inputs/Field";
export interface FormFieldProps extends FieldProps {
label: string,
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
}
export function FormField({label, validity, pairProps, labelProps, ...props}: FormFieldProps): JSX.Element {
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={<Field {...props}/>}
validity={validity}
{...pairProps}
/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface FormGroupProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function FormGroup({...props}: FormGroupProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "form-group")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface FormLabelProps extends Types.BluelibHTMLProps<HTMLLabelElement> {}
export function FormLabel({...props}: FormLabelProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "form-label")
return (
<BaseElement kind={"label"} {...props}/>
)
}

View file

@ -0,0 +1,35 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormMultiselect as FormMultiselectComponent } from "./FormMultiselect"
import { Option } from "../inputs/Option"
export default {
component: FormMultiselectComponent,
title: "Forms/Form Multiselect",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const FormMultiselect = props => (
<FormMultiselectComponent {...props}>
<Option value={"Red"}/>
<Option value={"Orange"}/>
<Option value={"Yellow"}/>
<Option value={"Green"}/>
<Option value={"Cyan"}/>
<Option value={"Blue"}/>
<Option value={"Purple"}/>
<Option value={"White"}/>
<Option value={"Black"}/>
<Option value={"Grey"}/>
</FormMultiselectComponent>
)
FormMultiselect.args = {
label: "Favourite colors",
}

View file

@ -0,0 +1,33 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {Multiselect, MultiselectProps} from "../inputs/Multiselect";
export interface FormMultiselectProps extends MultiselectProps {
label: string,
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
}
export function FormMultiselect({label, validity, pairProps, labelProps, ...props}: FormMultiselectProps): JSX.Element {
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={<Multiselect {...props}/>}
validity={validity}
{...pairProps}
/>
)
}
FormMultiselect.Option = Multiselect.Option

View file

@ -0,0 +1,60 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as UUID from "uuid"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormLabel} from "./FormLabel";
import {FormGroup} from "./FormGroup";
export interface FormPairProps extends Types.BluelibProps {
label: JSX.Element,
input: JSX.Element,
// Validity is in the form pair so it can be propagated to both the label and the group
validity?: Types.Validity,
id?: string,
}
export function FormPair({id, label, input, validity, bluelibClassNames, customColor, disabled}: FormPairProps): JSX.Element {
if(!id) {
id = UUID.v4()
}
let validityClass
// If the input is valid
if(validity === true) {
validityClass = "color-lime"
}
// If the input is invalid
else if(validity === false) {
validityClass = "color-red"
}
// If the input has no validity
else if(validity === null) {
validityClass = ""
}
// If no validity has been passed
else {
validityClass = ""
}
label = React.cloneElement(label, {
htmlFor: id,
bluelibClassNames: mergeClassNames(bluelibClassNames, validityClass),
customColor: customColor,
})
input = React.cloneElement(input, {
id: id,
bluelibClassNames: mergeClassNames(bluelibClassNames, validityClass),
customColor: customColor,
disabled: disabled,
})
return <>
{label}
{input}
</>
}

View file

@ -0,0 +1,26 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormRadioGroup as FormRadioGroupComponent } from "./FormRadioGroup"
export default {
component: FormRadioGroupComponent,
title: "Forms/Form Radio Group",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const FormRadioGroup = props => (
<FormRadioGroupComponent {...props}/>
)
FormRadioGroup.args = {
label: "Size",
options: ["XS", "S", "M", "L", "XL"],
row: false,
disabled: false,
}

View file

@ -0,0 +1,75 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import * as UUID from "uuid"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {FormGroup, FormGroupProps} from "./FormGroup";
import {Radio} from "../inputs/Radio";
import {LabelledRadio, LabelledRadioProps} from "../inputs/LabelledRadio";
export interface FormRadioGroupProps extends Types.BluelibProps {
name?: string,
label: string,
options: string[],
row?: boolean,
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
onSimpleChange?: (value: string) => void,
value?: string,
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
groupProps?: FormGroupProps,
radioProps?: LabelledRadioProps,
}
export function FormRadioGroup({name, label, options, row, onChange, onSimpleChange, value, validity, pairProps, labelProps, groupProps, radioProps, disabled, bluelibClassNames, customColor}: FormRadioGroupProps): JSX.Element {
if(!name) {
name = UUID.v4()
}
const radios = options.map<JSX.Element>(option => (
<LabelledRadio
label={option}
value={option}
row={row}
checked={value ? value === option : undefined}
name={name}
disabled={disabled}
{...radioProps}
/>
))
const onChangeWrapped = React.useCallback(
event => {
if(onChange) onChange(event)
if(onSimpleChange) onSimpleChange(event.target.value)
},
[onChange, onSimpleChange]
)
const group = (
<FormGroup onChange={onChangeWrapped} value={value} {...groupProps}>
{radios}
</FormGroup>
)
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={group}
validity={validity}
bluelibClassNames={bluelibClassNames}
customColor={customColor}
{...pairProps}
/>
)
}

View file

@ -0,0 +1,32 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormRow as FormRowComponent } from "./FormRow"
import { Parenthesis } from "../panels/Parenthesis"
import { Button } from "../inputs/Button"
import { Separator } from "../separators/Separator"
export default {
component: FormRowComponent,
title: "Forms/Form Row",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
}
export const WithText = props => (
<FormRowComponent {...props}>
<Parenthesis>By logging in, you accept our Terms of Service.</Parenthesis>
</FormRowComponent>
)
WithText.args = {}
export const WithButtons = props => (
<FormRowComponent {...props}>
<Button>Cancel</Button>
<Button>Retry</Button>
<Button>Ignore</Button>
</FormRowComponent>
)
WithButtons.args = {}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface FormRowProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function FormRow({...props}: FormRowProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "form-row")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,28 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { FormSelect as FormSelectComponent } from "./FormSelect"
import { Option } from "../inputs/Option"
export default {
component: FormSelectComponent,
title: "Forms/Form Select",
decorators: [Decorators.Form, Decorators.Box, Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const FormSelect = props => (
<FormSelectComponent {...props}>
<Option value={"I'm ready!"}/>
<Option value={"Please wait..."}/>
<Option value={"I won't be there."}/>
</FormSelectComponent>
)
FormSelect.args = {
label: "Ready check",
}

View file

@ -0,0 +1,32 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {FormPair, FormPairProps} from "./FormPair";
import {FormLabel, FormLabelProps} from "./FormLabel";
import {Select, SelectProps} from "../inputs/Select";
export interface FormSelectProps extends SelectProps {
label: string,
validity?: Types.Validity,
pairProps?: FormPairProps,
labelProps?: FormLabelProps,
}
export function FormSelect({label, validity, pairProps, labelProps, ...props}: FormSelectProps): JSX.Element {
return (
<FormPair
label={<FormLabel {...labelProps}>{label}</FormLabel>}
input={<Select {...props}/>}
validity={validity}
{...pairProps}
/>
)
}
FormSelect.Option = Select.Option

View file

@ -17,25 +17,28 @@ export default {
} }
export const NoLimit = props => ( export const FullLimit = props => (
<Image {...props}/> <Image {...props}/>
) )
NoLimit.args = { FullLimit.args = {
src: PineappleWithSunglasses, src: PineappleWithSunglasses,
limit: "no", limit: "no",
disabled: false, disabled: false,
} }
export const HalfLimit = NoLimit.bind({}) export const HalfLimit = FullLimit.bind({})
HalfLimit.args = { HalfLimit.args = {
...NoLimit.args, ...FullLimit.args,
limit: "half", limit: "half",
} }
export const QuarterLimit = NoLimit.bind({}) export const QuarterLimit = FullLimit.bind({})
QuarterLimit.args = { QuarterLimit.args = {
...NoLimit.args, ...FullLimit.args,
limit: "quarter", limit: "quarter",
} }

View file

@ -5,29 +5,25 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface ImageProps { export interface ImageProps extends Types.BluelibHTMLProps<HTMLImageElement> {
src: string, limit?: "full" | "half" | "quarter",
limit?: "no" | "half" | "quarter",
[props: string]: any,
} }
const LIMIT_CLASSES = { const LIMIT_CLASSES = {
no: "", full: "",
half: "image-limit-half", half: "image-limit-half",
quarter: "image-limit-quarter", quarter: "image-limit-quarter",
} }
export function Image({limit = "no", ...props}: ImageProps): JSX.Element { export function Image({limit = "full", ...props}: ImageProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "image") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "image")
if(limit) { if(limit) {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, LIMIT_CLASSES[limit]) props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, LIMIT_CLASSES[limit])
} }
// TODO: Capture the src and make the image clickable
return ( return (
<BaseElement kind={"img"} {...props}/> <BaseElement kind={"img"} {...props}/>
) )

View file

@ -1,25 +1,24 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Area } from "./Area" import { Area as AreaComponent } from "./Area"
export default { export default {
component: Area, component: AreaComponent,
title: "Inputs/Area", title: "Inputs/Area",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Area = props => (
<Area {...props}/> <AreaComponent {...props}/>
) )
Default.args = { Area.args = {
placeholder: "Enter multiline text here\n\nThis component can be resized", placeholder: "Enter multiline text here\n\nThis component can be resized",
disabled: false, disabled: false,
required: false, required: false,

View file

@ -5,37 +5,25 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface AreaProps { export interface AreaProps extends Types.BluelibHTMLProps<HTMLTextAreaElement> {
placeholder: string, onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void,
required?: boolean, onSimpleChange?: (value: string) => void,
disabled?: boolean,
onChange: (contents: string) => boolean,
value?: string, value?: string,
[props: string]: any,
} }
export function Area({onChange, ...props}: AreaProps): JSX.Element { export function Area({onChange, onSimpleChange, ...props}: AreaProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-area") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-area")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLTextAreaElement>): boolean => { if(onChange) onChange(event)
const contents = event.target.value if(onSimpleChange) onSimpleChange(event.target.value)
if(onChange) {
return onChange(contents)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"textarea"} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"textarea"} onChange={onChangeWrapped} {...props}/>
) )
} }

View file

@ -0,0 +1,24 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { Button as ButtonComponent } from "./Button"
export default {
component: ButtonComponent,
title: "Inputs/Button",
decorators: [Decorators.Bluelib],
argTypes: {
onClick: {action: "Click"},
},
}
export const Button = ({ children, ...props }) => {
// Weird thing to get IntelliJ to understand that this component is fine
return <ButtonComponent {...props}>{children}</ButtonComponent>
}
Button.args = {
children: "Click me!",
disabled: false,
}

View file

@ -0,0 +1,23 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface ButtonProps extends Types.BluelibHTMLProps<HTMLButtonElement> {
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void,
children: React.ReactNode,
}
export function Button({onClick, disabled, children, ...props}: ButtonProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-button")
const onClickWrapped = disabled ? () => {} : onClick
return (
<BaseElement kind={"button"} type={"button"} onClick={onClickWrapped} disabled={disabled} {...props}>{children}</BaseElement>
)
}

View file

@ -1,50 +1,26 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Checkbox } from "./Checkbox" import { Checkbox as CheckboxComponent } from "./Checkbox"
import { Box } from "../panels/Box" import { Box } from "../panels/Box"
import { BaseElement } from "../BaseElement" import { BaseElement } from "../BaseElement"
export default { export default {
component: Checkbox, component: CheckboxComponent,
title: "Inputs/Checkbox", title: "Inputs/Checkbox",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Checkbox = props => (
<Checkbox value={"zero"} {...props}/> <CheckboxComponent {...props}/>
) )
Checkbox.args = {
name: "what",
value: "this-checkbox-right-here",
export const ThreeCheckboxes = props => (
<BaseElement kind={"div"}>
<BaseElement kind={"div"} bluelibClassNames={"color-blue"}>
<Checkbox value={"suicune"} {...props}/> Suicune
</BaseElement>
<BaseElement kind={"div"} bluelibClassNames={"color-yellow"}>
<Checkbox value={"raikou"} {...props}/> Raikou
</BaseElement>
<BaseElement kind={"div"} bluelibClassNames={"color-red"}>
<Checkbox value={"entei"} {...props}/> Entei
</BaseElement>
</BaseElement>
)
ThreeCheckboxes.args = {
name: "example"
}
ThreeCheckboxes.argTypes = {
customColor: {
control: {type: "color"},
},
value: {
control: {type: "null"},
},
} }

View file

@ -5,38 +5,26 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface CheckboxProps { export interface CheckboxProps extends Types.BluelibHTMLProps<HTMLInputElement> {
disabled?: boolean, onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
onSimpleChange?: (value: string, checked: boolean) => void,
onChange?: (value: string, checked: boolean) => boolean, checked?: boolean,
name: string,
value: string, value: string,
[props: string]: any,
} }
export function Checkbox({onChange, ...props}: CheckboxProps): JSX.Element { export function Checkbox({onChange, onSimpleChange, ...props}: CheckboxProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-checkbox") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-checkbox")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLInputElement>): boolean => { if(onChange) onChange(event)
const checked = event.target.checked if(onSimpleChange) onSimpleChange(event.target.value, event.target.checked)
const value = event.target.value
if(onChange) {
return onChange(value, checked)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"input"} type={"checkbox"} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"input"} type={"checkbox"} onChange={onChangeWrapped} {...props}/>
) )
} }

View file

@ -1,25 +1,24 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Field } from "./Field" import { Field as FieldComponent } from "./Field"
export default { export default {
component: Field, component: FieldComponent,
title: "Inputs/Field", title: "Inputs/Field",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Field = props => (
<Field {...props}/> <FieldComponent {...props}/>
) )
Default.args = { Field.args = {
placeholder: "Enter text here", placeholder: "Enter text here",
disabled: false, disabled: false,
required: false, required: false,

View file

@ -5,37 +5,25 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface FieldProps { export interface FieldProps extends Types.BluelibHTMLProps<HTMLInputElement> {
placeholder: string, onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
required?: boolean, onSimpleChange?: (value: string) => void,
disabled?: boolean,
onChange: (contents: string) => boolean,
value?: string, value?: string,
[props: string]: any,
} }
export function Field({onChange, value, ...props}: FieldProps): JSX.Element { export function Field({onChange, onSimpleChange, ...props}: FieldProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-field") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-field")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLInputElement>): boolean => { if(onChange) onChange(event)
const contents = event.target.value if(onSimpleChange) onSimpleChange(event.target.value)
if(onChange) {
return onChange(contents)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"input"} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"input"} onChange={onChangeWrapped} {...props}/>
) )
} }

View file

@ -0,0 +1,15 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface InputLabelProps extends Types.BluelibHTMLProps<HTMLLabelElement> {}
export function InputLabel({...props}: InputLabelProps): JSX.Element {
return (
<BaseElement kind={"label"} {...props}/>
)
}

View file

@ -0,0 +1,25 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { LabelledCheckbox as LabelledCheckboxComponent } from "./LabelledCheckbox"
export default {
component: LabelledCheckboxComponent,
title: "Inputs/Labelled Checkbox",
decorators: [Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const LabelledCheckbox = props => (
<LabelledCheckboxComponent {...props}/>
)
LabelledCheckbox.args = {
label: "This",
name: "what",
value: "this",
}

View file

@ -0,0 +1,26 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {InputLabel, InputLabelProps} from "./InputLabel";
import {Checkbox, CheckboxProps} from "./Checkbox";
export interface LabelledCheckboxProps extends CheckboxProps {
label: string,
labelProps?: InputLabelProps,
row?: boolean,
}
export function LabelledCheckbox({label, labelProps = {}, row, ...props}: LabelledCheckboxProps): JSX.Element {
labelProps.bluelibClassNames = mergeClassNames(props.bluelibClassNames, row ? "form-group-row": "")
return (
<InputLabel {...labelProps}>
<Checkbox {...props}/>
&nbsp;{label}
</InputLabel>
)
}

View file

@ -0,0 +1,25 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { LabelledRadio as LabelledRadioComponent } from "./LabelledRadio"
export default {
component: LabelledRadioComponent,
title: "Inputs/Labelled Radio",
decorators: [Decorators.Bluelib],
argTypes: {
onChange: {action: "Change"},
onSimpleChange: {action: "SimpleChange"},
},
}
export const LabelledRadio = props => (
<LabelledRadioComponent {...props}/>
)
LabelledRadio.args = {
label: "This",
name: "what",
value: "this",
}

View file

@ -0,0 +1,26 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {Radio, RadioProps} from "./Radio";
import {InputLabel, InputLabelProps} from "./InputLabel";
export interface LabelledRadioProps extends RadioProps {
label: string,
labelProps?: InputLabelProps,
row?: boolean,
}
export function LabelledRadio({label, labelProps = {}, row, ...props}: LabelledRadioProps): JSX.Element {
labelProps.bluelibClassNames = mergeClassNames(props.bluelibClassNames, row ? "form-group-row": "")
return (
<InputLabel {...labelProps}>
<Radio {...props}/>
&nbsp;{label}
</InputLabel>
)
}

View file

@ -8,40 +8,40 @@ import { Multiselect } from "./Multiselect"
export default { export default {
component: Multiselect, component: Multiselect,
subcomponents: {Option, OptionGroup},
title: "Inputs/Multiselect", title: "Inputs/Multiselect",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Basic = props => (
<Multiselect {...props}> <Multiselect {...props}>
<Option label={"Yes"}/> <Multiselect.Option value={"Yes"}/>
<Option label={"Maybe"}/> <Multiselect.Option value={"Maybe"}/>
<Option label={"No"}/> <Multiselect.Option value={"No"}/>
</Multiselect> </Multiselect>
) )
Default.args = { Basic.args = {
disabled: false, disabled: false,
} }
export const WithGroups = props => ( export const WithGroups = props => (
<Multiselect {...props}> <Multiselect {...props}>
<OptionGroup label={"A"}> <Multiselect.Group label={"A"}>
<Option label={"Anchor"}/> <Multiselect.Option value={"Anchor"}/>
<Option label={"Angel"}/> <Multiselect.Option value={"Angel"}/>
<Option label={"Anti-air"}/> <Multiselect.Option value={"Anti-air"}/>
</OptionGroup> </Multiselect.Group>
<OptionGroup label={"B"}> <Multiselect.Group label={"B"}>
<Option label={"Banana"}/> <Multiselect.Option value={"Banana"}/>
<Option label={"Boat"}/> <Multiselect.Option value={"Boat"}/>
<Option label={"Bus"}/> <Multiselect.Option value={"Bus"}/>
</OptionGroup> </Multiselect.Group>
</Multiselect> </Multiselect>
) )
WithGroups.args = { WithGroups.args = {

View file

@ -3,39 +3,33 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {Option} from "./Option";
import {OptionGroup} from "./OptionGroup";
interface MultiselectProps { export interface MultiselectProps extends Types.BluelibHTMLProps<HTMLSelectElement> {
disabled?: boolean, onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void,
onSimpleChange?: (value: string[]) => void,
onChange?: (contents: string[]) => boolean, value?: string[],
children: React.ReactNode,
[props: string]: any,
} }
export function Multiselect({onChange, ...props}: MultiselectProps): JSX.Element { export function Multiselect({onChange, onSimpleChange, ...props}: MultiselectProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-multiselect") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-multiselect")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLSelectElement>): boolean => { if(onChange) onChange(event)
const options = Array.from(event.target.selectedOptions) if(onSimpleChange) onSimpleChange(Array.from<HTMLOptionElement>(event.target.selectedOptions).map(option => option.value))
const contents = options.map((option: HTMLOptionElement) => option.value)
if(onChange) {
return onChange(contents)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"select"} multiple={true} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"select"} multiple={true} onChange={onChangeWrapped} {...props}/>
) )
} }
Multiselect.Option = Option
Multiselect.Group = OptionGroup

View file

@ -1,5 +0,0 @@
import * as React from "react"
import * as Types from "../../types"
export const MultiselectContext: Types.UseStateContext<string[]> = React.createContext(null) as Types.UseStateContext<string[]>

View file

@ -5,19 +5,19 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface OptionProps { export interface OptionProps {
label: string, value: string,
[props: string]: any, [props: string]: any,
} }
export function Option({label, ...props}: OptionProps): JSX.Element { export function Option({value, ...props}: OptionProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input-option") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input-option")
return ( return (
<BaseElement kind={"option"} {...props}> <BaseElement kind={"option"} {...props}>
{label} {value}
</BaseElement> </BaseElement>
) )
} }

View file

@ -5,7 +5,7 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface OptionGroupProps { export interface OptionGroupProps {
label: string, label: string,
[props: string]: any, [props: string]: any,

View file

@ -1,50 +1,26 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Radio } from "./Radio" import { Radio as RadioComponent } from "./Radio"
import { Box } from "../panels/Box" import { Box } from "../panels/Box"
import { BaseElement } from "../BaseElement" import { BaseElement } from "../BaseElement"
export default { export default {
component: Radio, component: RadioComponent,
title: "Inputs/Radio", title: "Inputs/Radio",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Radio = props => (
<Radio value={"zero"} {...props}/> <RadioComponent {...props}/>
) )
Radio.args = {
name: "what",
value: "this-radio-right-here",
export const ThreeRadios = props => (
<BaseElement kind={"div"}>
<BaseElement kind={"div"} bluelibClassNames={"color-blue"}>
<Radio value={"articuno"} {...props}/> Articuno
</BaseElement>
<BaseElement kind={"div"} bluelibClassNames={"color-yellow"}>
<Radio value={"zapdos"} {...props}/> Zapdos
</BaseElement>
<BaseElement kind={"div"} bluelibClassNames={"color-red"}>
<Radio value={"moltres"} {...props}/> Moltres
</BaseElement>
</BaseElement>
)
ThreeRadios.args = {
name: "example"
}
ThreeRadios.argTypes = {
customColor: {
control: {type: "color"},
},
value: {
control: {type: "null"},
},
} }

View file

@ -5,37 +5,26 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface RadioProps { export interface RadioProps extends Types.BluelibHTMLProps<HTMLInputElement> {
disabled?: boolean, onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
onSimpleChange?: (value: string) => void,
onChange?: (value: string) => boolean, checked?: boolean,
name: string,
value: string, value: string,
[props: string]: any,
} }
export function Radio({onChange, ...props}: RadioProps): JSX.Element { export function Radio({onChange, onSimpleChange, ...props}: RadioProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-radio") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-radio")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLInputElement>): boolean => { if(onChange) onChange(event)
const value = event.target.value if(onSimpleChange) onSimpleChange(event.target.value)
if(onChange) {
return onChange(value)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"input"} type={"radio"} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"input"} type={"radio"} onChange={onChangeWrapped} {...props}/>
) )
} }

View file

@ -1,49 +1,48 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Select } from "./Select" import { Select, Select as SelectComponent } from "./Select"
import { Option } from "./Option" import { Option } from "./Option"
import { OptionGroup } from "./OptionGroup" import { OptionGroup } from "./OptionGroup"
export default { export default {
component: Select, component: SelectComponent,
title: "Inputs/Select", title: "Inputs/Select",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: { argTypes: {
customColor: { onChange: {action: "Change"},
control: {type: "color"}, onSimpleChange: {action: "SimpleChange"},
},
}, },
} }
export const Default = props => ( export const Basic = props => (
<Select {...props}> <SelectComponent {...props}>
<Option label={"Yes"}/> <SelectComponent.Option value={"Yes"}/>
<Option label={"Maybe"}/> <SelectComponent.Option value={"Maybe"}/>
<Option label={"No"}/> <SelectComponent.Option value={"No"}/>
</Select> </SelectComponent>
) )
Default.args = { Basic.args = {
disabled: false, disabled: false,
} }
export const WithGroups = props => ( export const WithGroups = props => (
<Select {...props}> <SelectComponent {...props}>
<Option label={"Ungrouped"}/> <SelectComponent.Option value={"Ungrouped"}/>
<OptionGroup label={"A"}> <SelectComponent.Group label={"A"}>
<Option label={"Anchor"}/> <SelectComponent.Option value={"Anchor"}/>
<Option label={"Angel"}/> <SelectComponent.Option value={"Angel"}/>
<Option label={"Anti-air"}/> <SelectComponent.Option value={"Anti-air"}/>
</OptionGroup> </SelectComponent.Group>
<OptionGroup label={"B"}> <SelectComponent.Group label={"B"}>
<Option label={"Banana"}/> <SelectComponent.Option value={"Banana"}/>
<Option label={"Boat"}/> <SelectComponent.Option value={"Boat"}/>
<Option label={"Bus"}/> <SelectComponent.Option value={"Bus"}/>
</OptionGroup> </SelectComponent.Group>
</Select> </SelectComponent>
) )
WithGroups.args = { WithGroups.args = {
disabled: false, disabled: false,

View file

@ -3,40 +3,33 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {SelectContext} from "./SelectContext"; import {Option} from "./Option"
import {OptionGroup} from "./OptionGroup"
interface SelectProps { export interface SelectProps extends Types.BluelibHTMLProps<HTMLSelectElement> {
disabled?: boolean, onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void,
onSimpleChange?: (value: string) => void,
onChange?: (contents: string) => boolean, value?: string,
children: React.ReactNode,
[props: string]: any,
} }
export function Select({onChange, ...props}: SelectProps): JSX.Element { export function Select({onChange, onSimpleChange, ...props}: SelectProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-select") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "input", "input-select")
const onChangeWrapper = React.useCallback( const onChangeWrapped = React.useCallback(
event => {
(event: React.ChangeEvent<HTMLSelectElement>): boolean => { if(onChange) onChange(event)
const contents = event.target.value if(onSimpleChange) onSimpleChange(event.target.value)
if(onChange) {
return onChange(contents)
}
return false
}, },
[onChange, onSimpleChange]
[onChange]
) )
return ( return (
<BaseElement kind={"select"} multiple={false} onChange={onChangeWrapper} {...props}/> <BaseElement kind={"select"} multiple={false} required={true} onChange={onChangeWrapped} {...props}/>
) )
} }
Select.Option = Option
Select.Group = OptionGroup

View file

@ -1,5 +0,0 @@
import * as React from "react"
import * as Types from "../../types"
export const SelectContext: Types.UseStateContext<string> = React.createContext(null) as Types.UseStateContext<string>

View file

@ -5,14 +5,10 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface LayoutProps { export interface BaseLayoutProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
[props: string]: any,
}
export function BaseLayout({...props}: LayoutProps): JSX.Element { export function BaseLayout({...props}: BaseLayoutProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout") props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout")
return ( return (

View file

@ -1,13 +1,14 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { LayoutFill } from "./LayoutFill" import { LayoutFill as LayoutFillComponent } from "./LayoutFill"
import { Bluelib } from "../Bluelib" import { LayoutFillSingle } from "./LayoutFillSingle"
import { Box } from "../panels/Box" import { Box } from "../panels/Box"
export default { export default {
component: LayoutFill, component: LayoutFillComponent,
subcomponents: {LayoutFillSingle},
title: "Layouts/Layout Fill", title: "Layouts/Layout Fill",
decorators: [Decorators.Bluelib, Decorators.Fill], decorators: [Decorators.Bluelib, Decorators.Fill],
parameters: { parameters: {
@ -16,13 +17,13 @@ export default {
} }
export const Default = props => ( export const LayoutFill = props => (
<LayoutFill {...props}> <LayoutFillComponent {...props}>
<LayoutFill.Single> <LayoutFillComponent.Single>
<Box> <Box>
Single Single
</Box> </Box>
</LayoutFill.Single> </LayoutFillComponent.Single>
</LayoutFill> </LayoutFillComponent>
) )
Default.args = {} LayoutFill.args = {}

View file

@ -3,12 +3,11 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {BaseLayout} from "./BaseLayout"; import {BaseLayout, BaseLayoutProps} from "./BaseLayout";
import {LayoutFillSingle} from "./LayoutFillSingle";
interface LayoutFillProps { export interface LayoutFillProps extends BaseLayoutProps {}
[props: string]: any,
}
export function LayoutFill({...props}: LayoutFillProps): JSX.Element { export function LayoutFill({...props}: LayoutFillProps): JSX.Element {
@ -18,12 +17,4 @@ export function LayoutFill({...props}: LayoutFillProps): JSX.Element {
} }
interface LayoutFillSingleProps { LayoutFill.Single = LayoutFillSingle
[props: string]: any,
}
LayoutFill.Single = function({...props}: LayoutFillSingleProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-fill-single")
return <BaseElement kind={"div"} {...props}/>
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface LayoutFillSingleProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function LayoutFillSingle({...props}: LayoutFillSingleProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-fill-single")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -1,12 +1,16 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { LayoutThreeCol } from "./LayoutThreeCol" import { LayoutThreeCol as LayoutThreeColComponent } from "./LayoutThreeCol"
import { LayoutThreeColLeft } from "./LayoutThreeColLeft"
import { LayoutThreeColCenter } from "./LayoutThreeColCenter"
import { LayoutThreeColRight } from "./LayoutThreeColRight"
import { Box } from "../panels/Box" import { Box } from "../panels/Box"
export default { export default {
component: LayoutThreeCol, component: LayoutThreeColComponent,
subcomponents: {LayoutThreeColLeft, LayoutThreeColCenter, LayoutThreeColRight},
title: "Layouts/Layout Three Col", title: "Layouts/Layout Three Col",
decorators: [Decorators.Bluelib, Decorators.Fill], decorators: [Decorators.Bluelib, Decorators.Fill],
parameters: { parameters: {
@ -15,23 +19,23 @@ export default {
} }
export const Default = props => ( export const LayoutThreeCol = props => (
<LayoutThreeCol {...props}> <LayoutThreeColComponent {...props}>
<LayoutThreeCol.Left> <LayoutThreeColComponent.Left>
<Box> <Box>
Left Left
</Box> </Box>
</LayoutThreeCol.Left> </LayoutThreeColComponent.Left>
<LayoutThreeCol.Center> <LayoutThreeColComponent.Center>
<Box> <Box>
Center Center
</Box> </Box>
</LayoutThreeCol.Center> </LayoutThreeColComponent.Center>
<LayoutThreeCol.Right> <LayoutThreeColComponent.Right>
<Box> <Box>
Right Right
</Box> </Box>
</LayoutThreeCol.Right> </LayoutThreeColComponent.Right>
</LayoutThreeCol> </LayoutThreeColComponent>
) )
Default.args = {} LayoutThreeCol.args = {}

View file

@ -3,10 +3,13 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {BaseLayout} from "./BaseLayout"; import {BaseLayout, BaseLayoutProps} from "./BaseLayout";
import {LayoutThreeColLeft} from "./LayoutThreeColLeft";
import {LayoutThreeColCenter} from "./LayoutThreeColCenter";
import {LayoutThreeColRight} from "./LayoutThreeColRight";
interface LayoutThreeColProps { export interface LayoutThreeColProps extends BaseLayoutProps {
[props: string]: any, [props: string]: any,
} }
@ -20,37 +23,6 @@ export function LayoutThreeCol({...props}: LayoutThreeColProps): JSX.Element {
} }
interface LayoutThreeColLeftProps { LayoutThreeCol.Left = LayoutThreeColLeft
[props: string]: any, LayoutThreeCol.Center = LayoutThreeColCenter
} LayoutThreeCol.Right = LayoutThreeColRight
LayoutThreeCol.Left = function({...props}: LayoutThreeColLeftProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-left")
return <BaseElement kind={"div"} {...props}/>
}
interface LayoutThreeColCenterProps {
[props: string]: any,
}
LayoutThreeCol.Center = function({...props}: LayoutThreeColCenterProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-center")
return <BaseElement kind={"div"} {...props}/>
}
interface LayoutThreeColRightProps {
[props: string]: any,
}
LayoutThreeCol.Right = function({...props}: LayoutThreeColRightProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-right")
return <BaseElement kind={"div"} {...props}/>
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface LayoutThreeColCenterProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function LayoutThreeColCenter({...props}: LayoutThreeColCenterProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-center")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface LayoutThreeColLeftProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function LayoutThreeColLeft({...props}: LayoutThreeColLeftProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-left")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface LayoutThreeColRightProps extends Types.BluelibHTMLProps<HTMLDivElement> {}
export function LayoutThreeColRight({...props}: LayoutThreeColRightProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "layout-threecol-right")
return (
<BaseElement kind={"div"} {...props}/>
)
}

View file

@ -1,46 +0,0 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { DescriptionList } from "./DescriptionList"
export default {
component: DescriptionList,
title: "Lists/DescriptionList",
decorators: [Decorators.Bluelib],
argTypes: {
customColor: {
control: {type: "color"},
},
disabled: {
control: {type: "boolean"},
},
},
}
export const Default = props => (
<DescriptionList {...props}>
<DescriptionList.Key>
LOL
</DescriptionList.Key>
<DescriptionList.Value>
Laughing out loud
</DescriptionList.Value>
<DescriptionList.Key>
KEK
</DescriptionList.Key>
<DescriptionList.Value>
Equivalent to lol, but said by a member of the Horde
</DescriptionList.Value>
<DescriptionList.Key>
LUL
</DescriptionList.Key>
<DescriptionList.Value>
Equivalent to lol, used by twitch.tv users to send an emoticon with the face of TotalBiscuit
</DescriptionList.Value>
</DescriptionList>
)
Default.args = {
disabled: false,
}

View file

@ -1,47 +0,0 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface DescriptionListProps {
[props: string]: any,
}
export function DescriptionList({...props}: DescriptionListProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-description")
return (
<BaseElement kind={"dl"} {...props}/>
)
}
interface DescriptionListKeyProps {
[props: string]: any,
}
DescriptionList.Key = function({...props}: DescriptionListKeyProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-description-key")
return (
<BaseElement kind={"dt"} {...props}/>
)
}
interface DescriptionListValueProps {
[props: string]: any,
}
DescriptionList.Value = function({...props}: DescriptionListValueProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-description-value")
return (
<BaseElement kind={"dd"} {...props}/>
)
}

View file

@ -1,39 +0,0 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { List } from "./List"
export default {
component: List,
title: "Lists/List",
decorators: [Decorators.Bluelib],
argTypes: {
customColor: {
control: {type: "color"},
},
disabled: {
control: {type: "boolean"},
},
},
}
export const Unordered = props => (
<List {...props}>
<List.Item>Io</List.Item>
<List.Item>Gyrocopter</List.Item>
<List.Item>Chaos Knight</List.Item>
</List>
)
Unordered.args = {
disabled: false,
ordered: false,
}
export const Ordered = Unordered.bind({})
Ordered.args = {
...Unordered.args,
ordered: true,
}

View file

@ -1,36 +0,0 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface ListProps {
ordered: boolean,
[props: string]: any,
}
export function List({ordered, ...props}: ListProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list", ordered ? "list-ordered" : "list-unordered")
return (
<BaseElement kind={ordered ? "ol" : "ul"} {...props}/>
)
}
interface ListItemProps {
[props: string]: any,
}
List.Item = function({...props}: ListItemProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-item")
return (
<BaseElement kind={"li"} {...props}/>
)
}

View file

@ -0,0 +1,36 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { ListDescription as ListDescriptionComponent } from "./ListDescription"
export default {
component: ListDescriptionComponent,
title: "Lists/List Description",
decorators: [Decorators.Bluelib],
}
export const ListDescription = props => (
<ListDescriptionComponent {...props}>
<ListDescriptionComponent.Term>
LOL
</ListDescriptionComponent.Term>
<ListDescriptionComponent.Details>
Laughing out loud
</ListDescriptionComponent.Details>
<ListDescriptionComponent.Term>
KEK
</ListDescriptionComponent.Term>
<ListDescriptionComponent.Details>
Equivalent to lol, but said by a member of the Horde
</ListDescriptionComponent.Details>
<ListDescriptionComponent.Term>
LUL
</ListDescriptionComponent.Term>
<ListDescriptionComponent.Details>
Equivalent to lol, used by twitch.tv users to send an emoticon with the face of TotalBiscuit
</ListDescriptionComponent.Details>
</ListDescriptionComponent>
)
ListDescription.args = {}

View file

@ -0,0 +1,23 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {ListTerm} from "./ListTerm";
import {ListDetails} from "./ListDetails";
export interface ListDescriptionProps extends Types.BluelibHTMLProps<HTMLDListElement> {}
export function ListDescription({...props}: ListDescriptionProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list", "list-description")
return (
<BaseElement kind={"dl"} {...props}/>
)
}
ListDescription.Term = ListTerm
ListDescription.Details = ListDetails

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface ListDetailsProps extends Types.BluelibHTMLProps<HTMLElement> {}
export function ListDetails({...props}: ListDetailsProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-description-details")
return (
<BaseElement kind={"dd"} {...props}/>
)
}

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface ListItemProps extends Types.BluelibHTMLProps<HTMLLIElement> {}
export function ListItem({...props}: ListItemProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-item")
return (
<BaseElement kind={"li"} {...props}/>
)
}

View file

@ -0,0 +1,25 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { ListOrdered as ListOrderedComponent } from "./ListOrdered"
import { ListItem } from "./ListItem"
export default {
component: ListOrderedComponent,
subcomponents: {ListItem},
title: "Lists/List Ordered",
decorators: [Decorators.Bluelib],
}
export const ListOrdered = props => (
<ListOrderedComponent {...props}>
<ListOrderedComponent.Item>Gold</ListOrderedComponent.Item>
<ListOrderedComponent.Item>Silver</ListOrderedComponent.Item>
<ListOrderedComponent.Item>Bronze</ListOrderedComponent.Item>
<ListOrderedComponent.Item>Iron</ListOrderedComponent.Item>
<ListOrderedComponent.Item>Wood</ListOrderedComponent.Item>
</ListOrderedComponent>
)
ListOrdered.args = {}

View file

@ -0,0 +1,21 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {ListItem} from "./ListItem";
export interface ListOrderedProps extends Types.BluelibHTMLProps<HTMLOListElement> {}
export function ListOrdered({...props}: ListOrderedProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list", "list-ordered")
return (
<BaseElement kind={"ol"} {...props}/>
)
}
ListOrdered.Item = ListItem

View file

@ -0,0 +1,17 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
export interface ListTermProps extends Types.BluelibHTMLProps<HTMLElement> {}
export function ListTerm({...props}: ListTermProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list-description-term")
return (
<BaseElement kind={"dt"} {...props}/>
)
}

View file

@ -0,0 +1,24 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators"
import { ListUnordered as ListUnorderedComponent } from "./ListUnordered"
import { ListItem } from "./ListItem"
export default {
component: ListUnorderedComponent,
subcomponents: {ListItem},
title: "Lists/List Unordered",
decorators: [Decorators.Bluelib],
}
export const ListUnordered = props => (
<ListUnorderedComponent {...props}>
<ListUnorderedComponent.Item>Mario</ListUnorderedComponent.Item>
<ListUnorderedComponent.Item>Luigi</ListUnorderedComponent.Item>
<ListUnorderedComponent.Item>Baby Mario</ListUnorderedComponent.Item>
<ListUnorderedComponent.Item>Baby Luigi</ListUnorderedComponent.Item>
</ListUnorderedComponent>
)
ListUnordered.args = {}

View file

@ -0,0 +1,21 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {ListItem} from "./ListItem";
export interface ListUnorderedProps extends Types.BluelibHTMLProps<HTMLUListElement> {}
export function ListUnordered({...props}: ListUnorderedProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "list", "list-unordered")
return (
<BaseElement kind={"ul"} {...props}/>
)
}
ListUnordered.Item = ListItem

View file

@ -1,13 +1,11 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {Panel} from "./Panel"; import {Panel, PanelProps} from "./Panel";
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface BoxProps { export interface BoxProps extends PanelProps {}
[props: string]: any,
}
export function Box({...props}: BoxProps): JSX.Element { export function Box({...props}: BoxProps): JSX.Element {

View file

@ -1,13 +1,11 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {Panel} from "./Panel"; import {Panel, PanelProps} from "./Panel";
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface DialogProps { export interface DialogProps extends PanelProps {}
[props: string]: any,
}
export function Dialog({...props}: DialogProps): JSX.Element { export function Dialog({...props}: DialogProps): JSX.Element {

View file

@ -5,9 +5,7 @@ import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface PanelProps { export interface PanelProps extends Types.BluelibHTMLProps<HTMLElement> {}
[props: string]: any,
}
export function Panel({...props}: PanelProps): JSX.Element { export function Panel({...props}: PanelProps): JSX.Element {

View file

@ -1,13 +1,11 @@
import * as React from "react" import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {Panel} from "./Panel"; import {Panel, PanelProps} from "./Panel";
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
interface ParenthesisProps { export interface ParenthesisProps extends PanelProps {}
[props: string]: any,
}
export function Parenthesis({...props}: ParenthesisProps): JSX.Element { export function Parenthesis({...props}: ParenthesisProps): JSX.Element {

View file

@ -6,13 +6,8 @@ import { Separator } from "./Separator"
export default { export default {
component: Separator, component: Separator,
title: "Separator/Separator", title: "Separators/Separator",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: {
customColor: {
control: {type: "color"},
},
},
} }

View file

@ -2,20 +2,19 @@ import * as React from "react"
import * as ReactDOM from "react-dom" import * as ReactDOM from "react-dom"
import * as Decorators from "../../utils/Decorators" import * as Decorators from "../../utils/Decorators"
import { Table } from "./Table" import { Table } from "./Table"
import { TableCaption } from "./TableCaption"
import { TableHeader } from "./TableHeader"
import { TableBody } from "./TableBody"
import { TableFooter } from "./TableFooter"
import { TableRow } from "./TableRow"
import { TableCell } from "./TableCell"
export default { export default {
component: Table, component: Table,
subcomponents: {TableCaption, TableHeader, TableBody, TableFooter, TableRow, TableCell},
title: "Tables/Table", title: "Tables/Table",
decorators: [Decorators.Bluelib], decorators: [Decorators.Bluelib],
argTypes: {
customColor: {
control: {type: "color"},
},
disabled: {
control: {type: "boolean"},
},
},
} }
@ -150,7 +149,7 @@ export const TierList = props => (
S S
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
span &lt;span&gt;
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
@ -158,7 +157,7 @@ export const TierList = props => (
A A
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
a &lt;a&gt;
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
@ -166,7 +165,7 @@ export const TierList = props => (
B B
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
body &lt;body&gt;
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
@ -174,7 +173,7 @@ export const TierList = props => (
C C
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
caption &lt;caption&gt;
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
</Table.Body> </Table.Body>

View file

@ -3,11 +3,15 @@ import * as ReactDOM from "react-dom"
import * as Types from "../../types" import * as Types from "../../types"
import {BaseElement} from "../BaseElement" import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames" import mergeClassNames from "classnames"
import {TableCaption} from "./TableCaption";
import {TableHeader} from "./TableHeader";
import {TableBody} from "./TableBody";
import {TableFooter} from "./TableFooter";
import {TableRow} from "./TableRow";
import {TableCell} from "./TableCell";
interface TableProps { interface TableProps extends Types.BluelibHTMLProps<HTMLTableElement> {}
[props: string]: any,
}
export function Table({...props}: TableProps): JSX.Element { export function Table({...props}: TableProps): JSX.Element {
@ -18,97 +22,9 @@ export function Table({...props}: TableProps): JSX.Element {
) )
} }
Table.Caption = TableCaption
interface TableCaptionProps { Table.Header = TableHeader
position: "top" | "bottom", Table.Body = TableBody
Table.Footer = TableFooter
[props: string]: any, Table.Row = TableRow
} Table.Cell = TableCell
const TABLE_CAPTION_CLASSES = {
top: "table-caption-top",
bottom: "table-caption-bottom",
}
Table.Caption = function({position, ...props}: TableCaptionProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-caption", TABLE_CAPTION_CLASSES[position])
return (
<BaseElement kind={"caption"} {...props}/>
)
}
interface TableHeaderProps {
[props: string]: any,
}
Table.Header = function ({position, ...props}: TableHeaderProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-header")
return (
<BaseElement kind={"thead"} {...props}/>
)
}
interface TableBodyProps {
[props: string]: any,
}
Table.Body = function ({position, ...props}: TableBodyProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-body")
return (
<BaseElement kind={"tbody"} {...props}/>
)
}
interface TableFooterProps {
[props: string]: any,
}
Table.Footer = function ({position, ...props}: TableFooterProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-footer")
return (
<BaseElement kind={"tfoot"} {...props}/>
)
}
interface TableRowProps {
[props: string]: any,
}
Table.Row = function ({...props}: TableRowProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-row")
return (
<BaseElement kind={"tr"} {...props}/>
)
}
interface TableCellProps {
head?: boolean,
mark?: boolean,
[props: string]: any,
}
Table.Cell = function ({head = false, mark = false, ...props}: TableCellProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, head ? "table-head" : "table-data", mark ? "table-mark" : "")
return (
<BaseElement kind={head ? "th" : "td"} {...props}/>
)
}

View file

@ -0,0 +1,19 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {Table} from "./Table";
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface TableBodyProps extends Types.BluelibHTMLProps<HTMLTableSectionElement> {}
export function TableBody({...props}: TableBodyProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-body")
return (
<BaseElement kind={"tbody"} {...props}/>
)
}

View file

@ -0,0 +1,26 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface TableCaptionProps extends Types.BluelibHTMLProps<HTMLTableCaptionElement> {
position: "top" | "bottom",
}
const TABLE_CAPTION_CLASSES = {
top: "table-caption-top",
bottom: "table-caption-bottom",
}
export function TableCaption({position = "top", ...props}: TableCaptionProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-caption", TABLE_CAPTION_CLASSES[position])
return (
<BaseElement kind={"caption"} {...props}/>
)
}

View file

@ -0,0 +1,23 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {Table} from "./Table";
interface TableCellProps extends Types.BluelibHTMLProps<HTMLTableCellElement> {
head?: boolean,
mark?: boolean,
[props: string]: any,
}
export function TableCell({head = false, mark = false, ...props}: TableCellProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, head ? "table-head" : "table-data", mark ? "table-mark" : "")
return (
<BaseElement kind={head ? "th" : "td"} {...props}/>
)
}

View file

@ -0,0 +1,19 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {Table} from "./Table";
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface TableFooterProps extends Types.BluelibHTMLProps<HTMLTableSectionElement> {}
export function TableFooter({...props}: TableFooterProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-footer")
return (
<BaseElement kind={"tfoot"} {...props}/>
)
}

View file

@ -0,0 +1,19 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {Table} from "./Table";
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
interface TableHeaderProps extends Types.BluelibHTMLProps<HTMLTableSectionElement> {}
export function TableHeader({...props}: TableHeaderProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-header")
return (
<BaseElement kind={"thead"} {...props}/>
)
}

View file

@ -0,0 +1,20 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import * as Types from "../../types"
import {BaseElement} from "../BaseElement"
import mergeClassNames from "classnames"
import {Table} from "./Table";
interface TableRowProps extends Types.BluelibHTMLProps<HTMLTableRowElement> {
[props: string]: any,
}
export function TableRow({...props}: TableRowProps): JSX.Element {
props.bluelibClassNames = mergeClassNames(props.bluelibClassNames, "table-row")
return (
<BaseElement kind={"tr"} {...props}/>
)
}

View file

@ -1,13 +1,24 @@
import * as React from "react"; import * as React from "react"
import Color from "color"
export type {Argument as ClassNames} from "classnames" import {Argument as ClassNamesArgument} from "classnames"
// export type IntrinsicComponentKind = JSX.IntrinsicElements
// export type FunctionComponentKind = (props: object) => JSX.Element
// export type ClassComponentKind = typeof React.Component
// export type ComponentKind = IntrinsicComponentKind | FunctionComponentKind | ClassComponentKind
export type ComponentKind = any
export type ClassNames = ClassNamesArgument
export type UseStateContext<S> = React.Context<null | [S, React.Dispatch<React.SetStateAction<S>>]>
export type State<Value> = [Value, React.Dispatch<React.SetStateAction<Value>>]
export type StateContext<Value> = React.Context<State<Value> | undefined>
export interface BluelibProps {
bluelibClassNames?: ClassNames,
customColor?: typeof Color,
disabled?: boolean,
}
export interface BluelibHTMLProps<Element extends HTMLElement> extends BluelibProps, React.HTMLProps<Element> {}
export type InputValue = readonly string[] | string | number | undefined
export type Validity = boolean | null

View file

@ -1,5 +1,6 @@
import { Bluelib as BluelibComponent } from "../components/Bluelib" import { Bluelib as BluelibComponent } from "../components/Bluelib"
import { Box as BoxComponent } from "../components/panels/Box" import { Box as BoxComponent } from "../components/panels/Box"
import { Form as FormComponent } from "../components/forms/Form"
export const Bluelib = Story => <BluelibComponent theme={"paper"} style={{backgroundColor: "transparent"}}><Story/></BluelibComponent> export const Bluelib = Story => <BluelibComponent theme={"paper"} style={{backgroundColor: "transparent"}}><Story/></BluelibComponent>
@ -7,3 +8,5 @@ export const Bluelib = Story => <BluelibComponent theme={"paper"} style={{backgr
export const Fill = Story => <div style={{height: "100vh"}}><Story/></div> export const Fill = Story => <div style={{height: "100vh"}}><Story/></div>
export const Box = Story => <BoxComponent><Story/></BoxComponent> export const Box = Story => <BoxComponent><Story/></BoxComponent>
export const Form = Story => <FormComponent><Story/></FormComponent>

View file

@ -3107,6 +3107,11 @@
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
"@types/uuid@^8.3.1":
version "8.3.1"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f"
integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==
"@types/webpack-env@^1.16.0": "@types/webpack-env@^1.16.0":
version "1.16.2" version "1.16.2"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa"
@ -13998,7 +14003,7 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.0: uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2" version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==