@@ -6,7 +6,9 @@ | |||||
"react/button-has-type": "off", | "react/button-has-type": "off", | ||||
"import/no-extraneous-dependencies": "off", | "import/no-extraneous-dependencies": "off", | ||||
"no-tabs": "off", | "no-tabs": "off", | ||||
"indent": "off" | |||||
"indent": "off", | |||||
"react/jsx-indent": "off", | |||||
"react/jsx-indent-props": "off" | |||||
}, | }, | ||||
"extends": [ | "extends": [ | ||||
"lxsmnsyc/typescript/react" | "lxsmnsyc/typescript/react" | ||||
@@ -18,6 +18,18 @@ const meta: Meta<typeof Component> = { | |||||
control: { type: 'select' }, | control: { type: 'select' }, | ||||
options: Button.AVAILABLE_SIZES, | options: Button.AVAILABLE_SIZES, | ||||
}, | }, | ||||
subtext: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
badge: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
icon: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
children: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
onClick: { | onClick: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -25,15 +37,18 @@ const meta: Meta<typeof Component> = { | |||||
action: 'clicked', | action: 'clicked', | ||||
}, | }, | ||||
}, | }, | ||||
args: Component.defaultProps ?? {}, | |||||
args: { | |||||
...(Component.defaultProps ?? {}), | |||||
subtext: 'Subtext', | |||||
badge: '1', | |||||
children: 'Button', | |||||
}, | |||||
}; | }; | ||||
export const ActionButton = (args: Omit<Props, 'ref'>) => ( | export const ActionButton = (args: Omit<Props, 'ref'>) => ( | ||||
<Component | <Component | ||||
{...args} | {...args} | ||||
> | |||||
Button | |||||
</Component> | |||||
/> | |||||
); | ); | ||||
export default meta; | export default meta; |
@@ -44,9 +44,13 @@ export interface ActionButtonProps extends Omit<React.HTMLProps<ActionButtonDeri | |||||
*/ | */ | ||||
compact?: boolean; | compact?: boolean; | ||||
/** | /** | ||||
* Should the children's height be variable? | |||||
* Graphical representation of the component. | |||||
*/ | */ | ||||
variableChildrenHeight?: boolean; | |||||
icon?: React.ReactNode; | |||||
/** | |||||
* Should the graphical representation of the component be placed after the children? | |||||
*/ | |||||
iconAfterChildren?: boolean; | |||||
} | } | ||||
/** | /** | ||||
@@ -66,7 +70,8 @@ export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionB | |||||
compact = false as const, | compact = false as const, | ||||
className, | className, | ||||
block = false as const, | block = false as const, | ||||
variableChildrenHeight = false as const, | |||||
icon, | |||||
iconAfterChildren = false as const, | |||||
...etcProps | ...etcProps | ||||
}, | }, | ||||
forwardedRef, | forwardedRef, | ||||
@@ -106,37 +111,52 @@ export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionB | |||||
> | > | ||||
<span | <span | ||||
className={clsx( | className={clsx( | ||||
'flex-auto min-w-0', | |||||
{ | |||||
'text-left': compact || menuItem, | |||||
'text-center': !(compact || menuItem), | |||||
}, | |||||
)} | |||||
'flex-auto min-w-0 flex items-center gap-2', | |||||
iconAfterChildren ? 'flex-row-reverse' : 'flex-row', | |||||
)} | |||||
> | > | ||||
<span | |||||
className={clsx( | |||||
'block uppercase font-bold w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded', | |||||
{ | |||||
'h-[1.1em]': !variableChildrenHeight, | |||||
}, | |||||
)} | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
{icon && ( | |||||
<span | |||||
data-testid="icon" | |||||
> | |||||
{icon} | |||||
</span> | |||||
)} | |||||
{(children || subtext) && ( | |||||
<span | |||||
className={clsx( | |||||
'min-w-0 flex-auto', | |||||
{ | |||||
'text-left': compact || menuItem, | |||||
'text-center': !(compact || menuItem), | |||||
}, | |||||
)} | |||||
> | |||||
{children && ( | |||||
<span | |||||
className={clsx( | |||||
'block uppercase font-bold w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded h-[1.1em]', | |||||
)} | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
)} | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
)} | |||||
</span> | </span> | ||||
{badge && ( | {badge && ( | ||||
<> | <> | ||||
@@ -177,5 +197,6 @@ ActionButton.defaultProps = { | |||||
menuItem: false as const, | menuItem: false as const, | ||||
size: 'medium' as const, | size: 'medium' as const, | ||||
compact: false as const, | compact: false as const, | ||||
variableChildrenHeight: false as const, | |||||
icon: undefined, | |||||
iconAfterChildren: false as const, | |||||
}; | }; |
@@ -10,6 +10,12 @@ const meta: Meta<typeof Component> = { | |||||
disable: true, | disable: true, | ||||
} | } | ||||
}, | }, | ||||
label: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
hint: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
onChange: { | onChange: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -17,7 +23,11 @@ const meta: Meta<typeof Component> = { | |||||
action: 'changed', | action: 'changed', | ||||
}, | }, | ||||
}, | }, | ||||
args: Component.defaultProps ?? {}, | |||||
args: { | |||||
...(Component.defaultProps ?? {}), | |||||
label: 'ファイル選択', | |||||
hint: 'ファイルを選択してください。', | |||||
}, | |||||
}; | }; | ||||
export const FileSelectBox = (args: Omit<Props, 'ref'>) => ( | export const FileSelectBox = (args: Omit<Props, 'ref'>) => ( | ||||
@@ -5,6 +5,21 @@ import { ComboBox as Component, ComboBoxProps as Props } from '.'; | |||||
const meta: Meta<typeof Component> = { | const meta: Meta<typeof Component> = { | ||||
component: Component, | component: Component, | ||||
argTypes: { | argTypes: { | ||||
label: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
hint: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
indicator: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
children: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
length: { | |||||
control: { type: 'number' }, | |||||
}, | |||||
onChange: { | onChange: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -18,10 +33,22 @@ const meta: Meta<typeof Component> = { | |||||
}, | }, | ||||
}; | }; | ||||
export const ComboBox = (args: Omit<Props, 'ref'>) => ( | |||||
export const ComboBox = ({ children = '', ...args }: Omit<Props, 'ref'>) => ( | |||||
<Component | <Component | ||||
{...args} | {...args} | ||||
/> | |||||
> | |||||
{(children ? children.toString() : '').split('\n').filter(s => s.length > 0).map((s, i) => { | |||||
const [label, value = label] = s.split(':').map(s => s.trim()) as string[]; | |||||
return ( | |||||
<option | |||||
key={`${s}:${i}`} | |||||
value={value} | |||||
> | |||||
{label} | |||||
</option> | |||||
); | |||||
})} | |||||
</Component> | |||||
); | ); | ||||
export default meta; | export default meta; |
@@ -5,6 +5,15 @@ import { DropdownSelect as Component, DropdownSelectProps as Props } from '.'; | |||||
const meta: Meta<typeof Component> = { | const meta: Meta<typeof Component> = { | ||||
component: Component, | component: Component, | ||||
argTypes: { | argTypes: { | ||||
label: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
hint: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
children: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
onChange: { | onChange: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -17,10 +26,22 @@ const meta: Meta<typeof Component> = { | |||||
}, | }, | ||||
}; | }; | ||||
export const DropdownSelect = (args: Omit<Props, 'ref'>) => ( | |||||
export const DropdownSelect = ({ children = '', ...args }: Omit<Props, 'ref'>) => ( | |||||
<Component | <Component | ||||
{...args} | {...args} | ||||
/> | |||||
> | |||||
{(children ? children.toString() : '').split('\n').filter(s => s.length > 0).map((s, i) => { | |||||
const [label, value = label] = s.split(':').map(s => s.trim()) as string[]; | |||||
return ( | |||||
<option | |||||
key={`${s}:${i}`} | |||||
value={value} | |||||
> | |||||
{label} | |||||
</option> | |||||
); | |||||
})} | |||||
</Component> | |||||
); | ); | ||||
export default meta; | export default meta; |
@@ -1,10 +1,22 @@ | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import type { Meta } from '@storybook/react'; | |||||
import { MenuSelect as Component, MenuSelectProps as Props } from '.'; | |||||
import type {Meta} from '@storybook/react'; | |||||
import {MenuSelect as Component, MenuSelectProps as Props} from '.'; | |||||
const meta: Meta<typeof Component> = { | const meta: Meta<typeof Component> = { | ||||
component: Component, | component: Component, | ||||
argTypes: { | argTypes: { | ||||
label: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
hint: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
indicator: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
children: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
onChange: { | onChange: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -17,10 +29,22 @@ const meta: Meta<typeof Component> = { | |||||
}, | }, | ||||
}; | }; | ||||
export const MenuSelect = (args: Omit<Props, 'ref'>) => ( | |||||
export const MenuSelect = ({ children = '', ...args }: Omit<Props, 'ref'>) => ( | |||||
<Component | <Component | ||||
{...args} | {...args} | ||||
/> | |||||
> | |||||
{(children ? children.toString() : '').split('\n').filter(s => s.length > 0).map((s, i) => { | |||||
const [label, value = label] = s.split(':').map(s => s.trim()) as string[]; | |||||
return ( | |||||
<option | |||||
key={`${s}:${i}`} | |||||
value={value} | |||||
> | |||||
{label} | |||||
</option> | |||||
); | |||||
})} | |||||
</Component> | |||||
); | ); | ||||
export default meta; | export default meta; |
@@ -2,7 +2,10 @@ | |||||
"root": true, | "root": true, | ||||
"rules": { | "rules": { | ||||
"react/jsx-props-no-spreading": "off", | "react/jsx-props-no-spreading": "off", | ||||
"no-tabs": "off" | |||||
"no-tabs": "off", | |||||
"indent": "off", | |||||
"react/jsx-indent": "off", | |||||
"react/jsx-indent-props": "off" | |||||
}, | }, | ||||
"extends": [ | "extends": [ | ||||
"lxsmnsyc/typescript/react" | "lxsmnsyc/typescript/react" | ||||
@@ -18,6 +18,21 @@ const meta: Meta<typeof Component> = { | |||||
control: { type: 'select' }, | control: { type: 'select' }, | ||||
options: Button.AVAILABLE_SIZES, | options: Button.AVAILABLE_SIZES, | ||||
}, | }, | ||||
subtext: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
badge: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
icon: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
children: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
href: { | |||||
control: { type: 'text' }, | |||||
}, | |||||
onClick: { | onClick: { | ||||
table: { | table: { | ||||
disable: true, | disable: true, | ||||
@@ -31,9 +46,7 @@ const meta: Meta<typeof Component> = { | |||||
export const LinkButton = (args: Omit<LinkButtonProps, 'ref'>) => ( | export const LinkButton = (args: Omit<LinkButtonProps, 'ref'>) => ( | ||||
<Component | <Component | ||||
{...args} | {...args} | ||||
> | |||||
Button | |||||
</Component> | |||||
/> | |||||
); | ); | ||||
export default meta; | export default meta; |
@@ -10,7 +10,7 @@ export type LinkButtonDerivedElement = HTMLAnchorElement; | |||||
/** | /** | ||||
* Props of the {@link LinkButton} component. | * Props of the {@link LinkButton} component. | ||||
*/ | */ | ||||
export interface LinkButtonProps<T = any> extends Omit<React.HTMLProps<LinkButtonDerivedElement>, 'size'> { | |||||
export interface LinkButtonProps<T = any> extends Omit<React.HTMLProps<LinkButtonDerivedElement>, 'href' | 'size'> { | |||||
/** | /** | ||||
* Should the component occupy the whole width of its parent? | * Should the component occupy the whole width of its parent? | ||||
*/ | */ | ||||
@@ -48,9 +48,17 @@ export interface LinkButtonProps<T = any> extends Omit<React.HTMLProps<LinkButto | |||||
*/ | */ | ||||
disabled?: boolean; | disabled?: boolean; | ||||
/** | /** | ||||
* Should the children's height be variable? | |||||
* Graphical representation of the component. | |||||
*/ | */ | ||||
variableChildrenHeight?: boolean; | |||||
icon?: React.ReactNode; | |||||
/** | |||||
* Should the graphical representation of the component be placed after the children? | |||||
*/ | |||||
iconAfterChildren?: boolean; | |||||
/** | |||||
* URL to navigate to. | |||||
*/ | |||||
href?: string | unknown; | |||||
} | } | ||||
/** | /** | ||||
@@ -71,7 +79,8 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||||
disabled = false, | disabled = false, | ||||
href, | href, | ||||
style, | style, | ||||
variableChildrenHeight = false as const, | |||||
icon, | |||||
iconAfterChildren = false as const, | |||||
...etcProps | ...etcProps | ||||
}, | }, | ||||
forwardedRef, | forwardedRef, | ||||
@@ -115,38 +124,53 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||||
style={style} | style={style} | ||||
> | > | ||||
<span | <span | ||||
className={clsx( | |||||
'flex-auto min-w-0', | |||||
{ | |||||
'text-left': compact || menuItem, | |||||
'text-center': !(compact || menuItem), | |||||
}, | |||||
)} | |||||
className={clsx( | |||||
'flex-auto min-w-0 flex items-center gap-2', | |||||
iconAfterChildren ? 'flex-row-reverse' : 'flex-row', | |||||
)} | |||||
> | > | ||||
<span | |||||
className={clsx( | |||||
'block uppercase font-bold w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded', | |||||
{ | |||||
'h-[1.1em]': !variableChildrenHeight, | |||||
}, | |||||
)} | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
{icon && ( | |||||
<span | |||||
data-testid="icon" | |||||
> | |||||
{icon} | |||||
</span> | |||||
)} | |||||
{(children || subtext) && ( | |||||
<span | |||||
className={clsx( | |||||
'min-w-0 flex-auto', | |||||
{ | |||||
'text-left': compact || menuItem, | |||||
'text-center': !(compact || menuItem), | |||||
}, | |||||
)} | |||||
> | |||||
{children && ( | |||||
<span | |||||
className={clsx( | |||||
'block uppercase font-bold w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded h-[1.1em]', | |||||
)} | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
)} | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
)} | |||||
</span> | </span> | ||||
{badge && ( | {badge && ( | ||||
<> | <> | ||||
@@ -189,5 +213,7 @@ LinkButton.defaultProps = { | |||||
subtext: undefined, | subtext: undefined, | ||||
block: false, | block: false, | ||||
disabled: false, | disabled: false, | ||||
variableChildrenHeight: false as const, | |||||
icon: undefined, | |||||
iconAfterChildren: false as const, | |||||
href: undefined, | |||||
}; | }; |
@@ -39,6 +39,39 @@ const config: Config = { | |||||
'inherit': 'inherit', | 'inherit': 'inherit', | ||||
'transparent': 'transparent', | 'transparent': 'transparent', | ||||
}, | }, | ||||
extend: { | |||||
fontSize: { | |||||
'lg': '1.125em', | |||||
'xl': '1.25em', | |||||
'2xl': '1.5em', | |||||
'3xl': '1.75em', | |||||
'4xl': '2em', | |||||
'5xl': '3em', | |||||
'6xl': '4em', | |||||
'xxs': '0.625rem', | |||||
}, | |||||
borderRadius: { | |||||
inherit: 'inherit', | |||||
}, | |||||
minWidth: { | |||||
6: '1.5rem', | |||||
10: '2.5rem', | |||||
12: '3rem', | |||||
16: '4rem', | |||||
48: '12rem', | |||||
64: '16rem', | |||||
}, | |||||
minHeight: { | |||||
6: '1.5rem', | |||||
10: '2.5rem', | |||||
12: '3rem', | |||||
16: '4rem', | |||||
64: '16rem', | |||||
}, | |||||
strokeWidth: { | |||||
3: '3', | |||||
}, | |||||
}, | |||||
}, | }, | ||||
plugins: [], | plugins: [], | ||||
}; | }; | ||||