| @@ -17,14 +17,25 @@ initialize(); | |||
| const preview: Preview = { | |||
| parameters: { | |||
| controls: { | |||
| expanded: true, | |||
| sort: 'requiredFirst', | |||
| matchers: { | |||
| color: /(background|color)$/i, | |||
| date: /Date$/i, | |||
| }, | |||
| }, | |||
| backgrounds: { | |||
| values: [ | |||
| { name: 'Dark', value: '#000' }, | |||
| { name: 'Gray', value: '#f9fafb' }, | |||
| { name: 'Light', value: '#FFF' }, | |||
| ], | |||
| default: 'Light', | |||
| }, | |||
| options: { | |||
| storySort: { | |||
| method: 'alphabetical', | |||
| order: ['Documentation', 'Components'], | |||
| }, | |||
| }, | |||
| }, | |||
| @@ -36,12 +36,14 @@ | |||
| "start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev max dev", | |||
| "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", | |||
| "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", | |||
| "storybook": "storybook dev -p 6006", | |||
| "storybook-build": "storybook build", | |||
| "storybook-docs": "storybook dev --docs", | |||
| "storybook-docs-build": "storybook build --docs", | |||
| "test": "jest", | |||
| "test:coverage": "npm run jest -- --coverage", | |||
| "test:update": "npm run jest -- -u", | |||
| "tsc": "tsc --noEmit", | |||
| "storybook": "storybook dev -p 6006", | |||
| "build-storybook": "storybook build" | |||
| "tsc": "tsc --noEmit" | |||
| }, | |||
| "lint-staged": { | |||
| "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", | |||
| @@ -166,4 +168,4 @@ | |||
| "public" | |||
| ] | |||
| } | |||
| } | |||
| } | |||
| @@ -8,15 +8,28 @@ import CodeSelectorModal from '@/components/CodeSelectorModal'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { Button } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | |||
| import './index.less'; | |||
| export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; | |||
| export { | |||
| requiredValidator, | |||
| type ParameterInputObject, | |||
| type ParameterInputValue, | |||
| } from '../ParameterInput'; | |||
| type CodeSelectProps = ParameterInputProps; | |||
| /** 代码配置选择表单组件 */ | |||
| function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||
| function CodeSelect({ | |||
| value, | |||
| size, | |||
| disabled, | |||
| className, | |||
| style, | |||
| onChange, | |||
| ...rest | |||
| }: CodeSelectProps) { | |||
| const selectResource = () => { | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| onOk: (res) => { | |||
| @@ -47,9 +60,10 @@ function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||
| }; | |||
| return ( | |||
| <div className="kf-code-select"> | |||
| <div className={classNames('kf-code-select', className)} style={style}> | |||
| <ParameterInput | |||
| {...rest} | |||
| size={size} | |||
| disabled={disabled} | |||
| value={value} | |||
| onChange={onChange} | |||
| @@ -57,7 +71,7 @@ function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||
| ></ParameterInput> | |||
| <Button | |||
| className="kf-code-select__button" | |||
| size="large" | |||
| size={size} | |||
| type="link" | |||
| icon={<KFIcon type="icon-xuanzedaimapeizhi" font={16} />} | |||
| disabled={disabled} | |||
| @@ -49,18 +49,31 @@ | |||
| padding: 10px 11px; | |||
| font-size: @font-size-input-lg; | |||
| .parameter-input__placeholder { | |||
| .parameter-input__placeholder, | |||
| .parameter-input__content__value { | |||
| min-height: 24px; | |||
| font-size: @font-size-input-lg; | |||
| line-height: 1.5; | |||
| } | |||
| .parameter-input__content__close-icon { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| .parameter-input.parameter-input--small { | |||
| padding: 0 7px; | |||
| font-size: @font-size-input; | |||
| .parameter-input__placeholder, | |||
| .parameter-input__content__value { | |||
| font-size: @font-size-input-lg; | |||
| line-height: 1.5; | |||
| min-height: 22px; | |||
| font-size: @font-size-input; | |||
| line-height: 1.5714285714285714; | |||
| } | |||
| .parameter-input__content__close-icon { | |||
| font-size: 12px; | |||
| font-size: 10px; | |||
| } | |||
| } | |||
| @@ -105,6 +105,7 @@ function ParameterInput({ | |||
| className={classNames( | |||
| 'parameter-input', | |||
| { 'parameter-input--large': size === 'large' }, | |||
| { 'parameter-input--small': size === 'small' }, | |||
| { [`parameter-input--${status}`]: status }, | |||
| className, | |||
| )} | |||
| @@ -12,12 +12,17 @@ import ResourceSelectorModal, { | |||
| } from '@/components/ResourceSelectorModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { Button } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | |||
| import './index.less'; | |||
| export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; | |||
| export { | |||
| requiredValidator, | |||
| type ParameterInputObject, | |||
| type ParameterInputValue, | |||
| } from '../ParameterInput'; | |||
| export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse }; | |||
| interface ResourceSelectProps extends ParameterInputProps { | |||
| @@ -31,7 +36,16 @@ const getSelectBtnIcon = (type: ResourceSelectorType) => { | |||
| }; | |||
| /** 数据集、模型、镜像选择表单组件 */ | |||
| function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) { | |||
| function ResourceSelect({ | |||
| type, | |||
| value, | |||
| size, | |||
| disabled, | |||
| className, | |||
| style, | |||
| onChange, | |||
| ...rest | |||
| }: ResourceSelectProps) { | |||
| const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | |||
| undefined, | |||
| ); | |||
| @@ -115,18 +129,19 @@ function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSe | |||
| }; | |||
| return ( | |||
| <div className="kf-resource-select"> | |||
| <div className={classNames('kf-resource-select', className)} style={style}> | |||
| <ParameterInput | |||
| {...rest} | |||
| disabled={disabled} | |||
| value={value} | |||
| size={size} | |||
| onChange={onChange} | |||
| onRemove={() => setSelectedResource(undefined)} | |||
| onClick={selectResource} | |||
| ></ParameterInput> | |||
| <Button | |||
| className="kf-resource-select__button" | |||
| size="large" | |||
| size={size} | |||
| type="link" | |||
| icon={getSelectBtnIcon(type)} | |||
| disabled={disabled} | |||
| @@ -1,4 +1,5 @@ | |||
| import CodeSelect from '@/components/CodeSelect'; | |||
| import CodeSelect, { type ParameterInputValue } from '@/components/CodeSelect'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { Col, Form, Row } from 'antd'; | |||
| @@ -35,6 +36,24 @@ type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| canInput: false, | |||
| textArea: false, | |||
| size: 'large', | |||
| style: { width: 400 }, | |||
| }, | |||
| render: function Render(args) { | |||
| const [{ value }, updateArgs] = useArgs(); | |||
| function handleChange(value?: ParameterInputValue) { | |||
| updateArgs({ value: value }); | |||
| args.onChange?.(value); | |||
| } | |||
| return <CodeSelect {...args} value={value} onChange={handleChange}></CodeSelect>; | |||
| }, | |||
| }; | |||
| export const InForm: Story = { | |||
| render: ({ onChange }) => { | |||
| return ( | |||
| <Form name="code-select-form" size="large"> | |||
| @@ -13,7 +13,7 @@ const meta = { | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| type: { control: 'select', options: Object.values(EmptyType) }, | |||
| type: { control: 'radio', options: Object.values(EmptyType) }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: { onButtonClick: fn() }, | |||
| @@ -1,7 +1,9 @@ | |||
| import ResourceSelect, { | |||
| ParameterInputValue, | |||
| requiredValidator, | |||
| ResourceSelectorType, | |||
| } from '@/components/ResourceSelect'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { Col, Form, Row } from 'antd'; | |||
| @@ -57,7 +59,7 @@ const meta = { | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| type: { control: 'radio', options: Object.values(ResourceSelectorType) }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: { onChange: fn() }, | |||
| @@ -68,6 +70,25 @@ type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| type: ResourceSelectorType.Dataset, | |||
| canInput: false, | |||
| textArea: false, | |||
| size: 'large', | |||
| style: { width: 400 }, | |||
| }, | |||
| render: function Render(args) { | |||
| const [{ value }, updateArgs] = useArgs(); | |||
| function handleChange(value?: ParameterInputValue) { | |||
| updateArgs({ value: value }); | |||
| args.onChange?.(value); | |||
| } | |||
| return <ResourceSelect {...args} value={value} onChange={handleChange}></ResourceSelect>; | |||
| }, | |||
| }; | |||
| export const InForm: Story = { | |||
| args: { | |||
| type: ResourceSelectorType.Dataset, | |||
| }, | |||
| @@ -82,7 +103,17 @@ export const Primary: Story = { | |||
| > | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="数据集" name="dataset"> | |||
| <Form.Item | |||
| label="数据集" | |||
| name="dataset" | |||
| rules={[ | |||
| { | |||
| validator: requiredValidator, | |||
| message: '请选择数据集', | |||
| }, | |||
| ]} | |||
| required | |||
| > | |||
| <ResourceSelect | |||
| type={ResourceSelectorType.Dataset} | |||
| placeholder="请选择" | |||
| @@ -95,17 +126,7 @@ export const Primary: Story = { | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="模型" | |||
| name="model" | |||
| rules={[ | |||
| { | |||
| validator: requiredValidator, | |||
| message: '请选择镜像', | |||
| }, | |||
| ]} | |||
| required | |||
| > | |||
| <Form.Item label="模型" name="model"> | |||
| <ResourceSelect | |||
| type={ResourceSelectorType.Model} | |||
| placeholder="请选择" | |||