mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added multiselect to AdminX Design System
refs. https://github.com/TryGhost/Team/issues/3318
This commit is contained in:
parent
e9ed7dfb64
commit
6900fd9eb0
4 changed files with 169 additions and 1 deletions
|
@ -76,6 +76,7 @@
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.24",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
|
"react-select": "^5.7.3",
|
||||||
"rollup-plugin-node-builtins": "2.1.2",
|
"rollup-plugin-node-builtins": "2.1.2",
|
||||||
"storybook": "7.0.18",
|
"storybook": "7.0.18",
|
||||||
"stylelint": "15.6.1",
|
"stylelint": "15.6.1",
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
|
import MultiSelect, {MultiSelectOption} from './MultiSelect';
|
||||||
|
import {MultiValue} from 'react-select';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Global / Select / Multiselect',
|
||||||
|
component: MultiSelect,
|
||||||
|
tags: ['autodocs']
|
||||||
|
} satisfies Meta<typeof MultiSelect>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof MultiSelect>;
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{value: 'steph', label: 'Steph Curry'},
|
||||||
|
{value: 'klay', label: 'Klay Thompson'},
|
||||||
|
{value: 'dray', label: 'Draymond Green'}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
options: options,
|
||||||
|
placeholder: 'Select your players'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Clear: Story = {
|
||||||
|
args: {
|
||||||
|
options: options,
|
||||||
|
clearBg: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Black: Story = {
|
||||||
|
args: {
|
||||||
|
options: options,
|
||||||
|
color: 'black'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithTitle: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Choose your players',
|
||||||
|
options: options,
|
||||||
|
color: 'black'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithTitleAndHint: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Choose your players',
|
||||||
|
options: options,
|
||||||
|
color: 'black',
|
||||||
|
hint: 'I knew you\'d choose all'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithDefaultValue: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Choose your players',
|
||||||
|
options: options,
|
||||||
|
color: 'black',
|
||||||
|
hint: 'I knew you\'d choose all',
|
||||||
|
defaultValues: [options[0]],
|
||||||
|
onChange: (selected: MultiValue<MultiSelectOption>) => {
|
||||||
|
selected?.map(o => (
|
||||||
|
alert(`${o.label} (${o.value})`)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
95
ghost/admin-x-settings/src/admin-x-ds/global/MultiSelect.tsx
Normal file
95
ghost/admin-x-settings/src/admin-x-ds/global/MultiSelect.tsx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import Heading from './Heading';
|
||||||
|
import Hint from './Hint';
|
||||||
|
import React from 'react';
|
||||||
|
import {MultiValue, default as ReactSelect, components} from 'react-select';
|
||||||
|
|
||||||
|
export type MultiSelectColor = 'grey' | 'black' | string;
|
||||||
|
|
||||||
|
export type MultiSelectOption = {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultiSelectProps {
|
||||||
|
options: MultiSelectOption[];
|
||||||
|
defaultValues?: MultiSelectOption[];
|
||||||
|
title?: string;
|
||||||
|
clearBg?: boolean;
|
||||||
|
error?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
color?: MultiSelectColor
|
||||||
|
hint?: string;
|
||||||
|
onChange: (selected: MultiValue<MultiSelectOption>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiSelect: React.FC<MultiSelectProps> = ({
|
||||||
|
title = '',
|
||||||
|
clearBg = false,
|
||||||
|
error = false,
|
||||||
|
placeholder,
|
||||||
|
color = 'grey',
|
||||||
|
hint = '',
|
||||||
|
options,
|
||||||
|
defaultValues,
|
||||||
|
onChange,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
let multiValueColor;
|
||||||
|
switch (color) {
|
||||||
|
case 'black':
|
||||||
|
multiValueColor = 'bg-black text-white';
|
||||||
|
break;
|
||||||
|
case 'grey':
|
||||||
|
multiValueColor = 'bg-grey-300 text-black';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customClasses = {
|
||||||
|
control: `w-full cursor-pointer appearance-none min-h-[40px] border-b ${!clearBg && 'bg-grey-75 px-[10px]'} py-2 outline-none ${error ? 'border-red' : 'border-grey-500 hover:border-grey-700'} ${title && 'mt-2'}`,
|
||||||
|
valueContainer: 'gap-1',
|
||||||
|
placeHolder: 'text-grey-600',
|
||||||
|
menu: 'shadow py-2 rounded-b z-50 bg-white',
|
||||||
|
option: 'hover:cursor-pointer hover:bg-grey-100 px-3 py-[6px]',
|
||||||
|
multiValue: `rounded-sm items-center text-[14px] py-px pl-2 pr-1 gap-1.5 ${multiValueColor}`,
|
||||||
|
noOptionsMessage: 'p-3 text-grey-600'
|
||||||
|
};
|
||||||
|
|
||||||
|
const DropdownIndicator: React.FC<any> = ddiProps => (
|
||||||
|
<components.DropdownIndicator {...ddiProps}>
|
||||||
|
<div className={`absolute top-[14px] block h-2 w-2 rotate-45 border-[1px] border-l-0 border-t-0 border-grey-900 content-[''] ${clearBg ? 'right-0' : 'right-4'} `}></div>
|
||||||
|
</components.DropdownIndicator>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
{title && <Heading useLabelTag={true}>{title}</Heading>}
|
||||||
|
<ReactSelect
|
||||||
|
classNames={{
|
||||||
|
menuList: () => 'z-50',
|
||||||
|
valueContainer: () => customClasses.valueContainer,
|
||||||
|
control: () => customClasses.control,
|
||||||
|
placeholder: () => customClasses.placeHolder,
|
||||||
|
menu: () => customClasses.menu,
|
||||||
|
option: () => customClasses.option,
|
||||||
|
multiValue: () => customClasses.multiValue,
|
||||||
|
noOptionsMessage: () => customClasses.noOptionsMessage
|
||||||
|
}}
|
||||||
|
components={{DropdownIndicator}}
|
||||||
|
defaultValue={defaultValues}
|
||||||
|
isClearable={false}
|
||||||
|
options={options}
|
||||||
|
placeholder={placeholder ? placeholder : ''}
|
||||||
|
isMulti
|
||||||
|
unstyled
|
||||||
|
onChange={onChange}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
{hint && <Hint color={error ? 'red' : ''}>{hint}</Hint>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultiSelect;
|
|
@ -4,7 +4,7 @@ import Select from './Select';
|
||||||
import {SelectOption} from './Select';
|
import {SelectOption} from './Select';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Select',
|
title: 'Global / Select / Simple select',
|
||||||
component: Select,
|
component: Select,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
decorators: [(_story: any) => (<div style={{maxWidth: '400px'}}>{_story()}</div>)],
|
decorators: [(_story: any) => (<div style={{maxWidth: '400px'}}>{_story()}</div>)],
|
||||||
|
|
Loading…
Add table
Reference in a new issue