| @@ -16,7 +16,7 @@ const config: StorybookConfig = { | |||
| name: '@storybook/react-webpack5', | |||
| options: {}, | |||
| }, | |||
| staticDirs: ['../public'], | |||
| staticDirs: ['../static'], | |||
| docs: { | |||
| defaultName: 'Documentation', | |||
| }, | |||
| @@ -21,6 +21,7 @@ | |||
| "incremental": true, // 通过读写磁盘上的文件来启用增量编译 | |||
| "noFallthroughCasesInSwitch": true, // 报告switch语句中的fallthrough案例错误 | |||
| "strictNullChecks": true, // 启用严格的null检查 | |||
| "importHelpers": true, | |||
| "baseUrl": "./" | |||
| } | |||
| } | |||
| @@ -166,7 +166,7 @@ | |||
| }, | |||
| "msw": { | |||
| "workerDirectory": [ | |||
| "public" | |||
| "static" | |||
| ] | |||
| } | |||
| } | |||
| @@ -4,7 +4,7 @@ | |||
| flex-wrap: wrap; | |||
| align-items: stretch; | |||
| width: 100%; | |||
| border: 1px solid @border-color-base; | |||
| border: 1px solid @border-color; | |||
| border-bottom: none; | |||
| border-radius: 4px; | |||
| @@ -12,7 +12,7 @@ | |||
| display: flex; | |||
| align-items: stretch; | |||
| width: 25%; | |||
| border-bottom: 1px solid @border-color-base; | |||
| border-bottom: 1px solid @border-color; | |||
| &__label { | |||
| flex: none; | |||
| @@ -1,21 +0,0 @@ | |||
| import { Typography } from 'antd'; | |||
| import styles from './index.less'; | |||
| type DisabledInputProps = { | |||
| value?: any; | |||
| valuePropName?: string; | |||
| }; | |||
| /** | |||
| * 模拟禁用的输入框,但是完全显示内容 | |||
| */ | |||
| function DisabledInput({ value, valuePropName }: DisabledInputProps) { | |||
| const data = valuePropName ? value[valuePropName] : value; | |||
| return ( | |||
| <div className={styles['disabled-input']}> | |||
| <Typography.Text ellipsis={{ tooltip: data }}>{data}</Typography.Text> | |||
| </div> | |||
| ); | |||
| } | |||
| export default DisabledInput; | |||
| @@ -1,4 +1,5 @@ | |||
| .disabled-input { | |||
| .form-info { | |||
| min-height: 32px; | |||
| padding: 4px 11px; | |||
| color: @text-disabled-color; | |||
| font-size: @font-size-input; | |||
| @@ -6,4 +7,14 @@ | |||
| border: 1px solid #d9d9d9; | |||
| border-radius: 6px; | |||
| cursor: not-allowed; | |||
| .ant-typography { | |||
| margin: 0 !important; | |||
| } | |||
| } | |||
| .form-info--multiline { | |||
| .ant-typography { | |||
| white-space: pre-wrap; | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| import { Typography } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import './index.less'; | |||
| type FormInfoProps = { | |||
| /** 自定义类名 */ | |||
| value?: any; | |||
| /** 如果 `value` 是对象时,取对象的哪个属性作为值 */ | |||
| valuePropName?: string; | |||
| /** 是否是多行 */ | |||
| multiline?: boolean; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| }; | |||
| /** | |||
| * 模拟禁用的输入框,但是内容超长时,hover 时显示所有内容 | |||
| */ | |||
| function FormInfo({ value, valuePropName, className, style, multiline = false }: FormInfoProps) { | |||
| const data = value && typeof value === 'object' && valuePropName ? value[valuePropName] : value; | |||
| return ( | |||
| <div | |||
| className={classNames( | |||
| 'form-info', | |||
| { | |||
| 'form-info--multiline': multiline, | |||
| }, | |||
| className, | |||
| )} | |||
| style={style} | |||
| > | |||
| <Typography.Paragraph ellipsis={multiline ? false : { tooltip: data }}> | |||
| {data} | |||
| </Typography.Paragraph> | |||
| </div> | |||
| ); | |||
| } | |||
| export default FormInfo; | |||
| @@ -4,7 +4,7 @@ | |||
| &__content { | |||
| padding: 20px @content-padding; | |||
| background-color: white; | |||
| border: 1px solid @border-color-base; | |||
| border: 1px solid @border-color; | |||
| border-top: none; | |||
| border-radius: 0 0 4px 4px; | |||
| } | |||
| @@ -22,8 +22,8 @@ | |||
| height: 398px; | |||
| margin-right: 15px; | |||
| padding: 15px; | |||
| background-color: @background-color-primary; | |||
| border: 1px solid @border-color; | |||
| background-color: rgba(22, 100, 255, 0.03); | |||
| border: 1px solid rgba(22, 100, 255, 0.3); | |||
| border-radius: 8px; | |||
| &__search { | |||
| @@ -31,7 +31,7 @@ | |||
| padding-left: 0; | |||
| background-color: transparent; | |||
| border-width: 0; | |||
| border-bottom: 1px solid @border-color-secondary; | |||
| border-bottom: 1px solid rgba(22, 100, 255, 0.1); | |||
| border-radius: 0; | |||
| } | |||
| @@ -45,8 +45,8 @@ | |||
| width: calc(100% - 488px - 15px); | |||
| height: 398px; | |||
| padding: 15px; | |||
| background-color: @background-color-primary; | |||
| border: 1px solid @border-color; | |||
| background-color: rgba(22, 100, 255, 0.03); | |||
| border: 1px solid rgba(22, 100, 255, 0.3); | |||
| border-radius: 8px; | |||
| &__title { | |||
| @@ -56,7 +56,7 @@ | |||
| color: @text-color; | |||
| font-size: @font-size; | |||
| line-height: 46px; | |||
| border-bottom: 1px solid @border-color-secondary; | |||
| border-bottom: 1px solid rgba(22, 100, 255, 0.1); | |||
| } | |||
| &__files { | |||
| height: calc(100% - 75px); | |||
| @@ -68,7 +68,7 @@ | |||
| color: @text-color-secondary; | |||
| font-size: 13px; | |||
| word-break: break-all; | |||
| background: @background-color-gray; | |||
| background: rgba(4, 3, 3, 0.06); | |||
| border-radius: 4px; | |||
| } | |||
| } | |||
| @@ -107,7 +107,7 @@ function ExperimentInstanceComponent({ | |||
| }; | |||
| if (!experimentInsList || experimentInsList.length === 0) { | |||
| return null; | |||
| return <div style={{ textAlign: 'center' }}>暂无实验实例</div>; | |||
| } | |||
| return ( | |||
| @@ -29,7 +29,7 @@ | |||
| div { | |||
| flex: 1; | |||
| height: 1px; | |||
| background-color: @border-color-base; | |||
| background-color: @border-color; | |||
| } | |||
| p { | |||
| @@ -45,7 +45,7 @@ | |||
| .ant-table-thead { | |||
| .ant-table-cell { | |||
| background-color: rgb(247, 247, 247); | |||
| border-color: @border-color-base !important; | |||
| border-color: @border-color !important; | |||
| } | |||
| } | |||
| .ant-table-tbody { | |||
| @@ -20,7 +20,7 @@ import TensorBoardStatusCell from '../TensorBoardStatus'; | |||
| import styles from './index.less'; | |||
| type ExperimentInstanceProps = { | |||
| experimentInList?: ExperimentInstance[]; | |||
| experimentInsList?: ExperimentInstance[]; | |||
| experimentInsTotal: number; | |||
| onClickInstance?: (instance: ExperimentInstance) => void; | |||
| onClickTensorBoard?: (instance: ExperimentInstance) => void; | |||
| @@ -30,7 +30,7 @@ type ExperimentInstanceProps = { | |||
| }; | |||
| function ExperimentInstanceComponent({ | |||
| experimentInList, | |||
| experimentInsList, | |||
| experimentInsTotal, | |||
| onClickInstance, | |||
| onClickTensorBoard, | |||
| @@ -40,8 +40,8 @@ function ExperimentInstanceComponent({ | |||
| }: ExperimentInstanceProps) { | |||
| const { message } = App.useApp(); | |||
| const allIntanceIds = useMemo(() => { | |||
| return experimentInList?.map((item) => item.id) || []; | |||
| }, [experimentInList]); | |||
| return experimentInsList?.map((item) => item.id) || []; | |||
| }, [experimentInsList]); | |||
| const [ | |||
| selectedIns, | |||
| setSelectedIns, | |||
| @@ -57,7 +57,7 @@ function ExperimentInstanceComponent({ | |||
| if (allIntanceIds.length === 0) { | |||
| setSelectedIns([]); | |||
| } | |||
| }, [experimentInList]); | |||
| }, [experimentInsList]); | |||
| // 删除实验实例确认 | |||
| const handleRemove = (instance: ExperimentInstance) => { | |||
| @@ -118,8 +118,8 @@ function ExperimentInstanceComponent({ | |||
| } | |||
| }; | |||
| if (!experimentInList || experimentInList.length === 0) { | |||
| return null; | |||
| if (!experimentInsList || experimentInsList.length === 0) { | |||
| return <div style={{ textAlign: 'center' }}>暂无数据</div>; | |||
| } | |||
| return ( | |||
| @@ -152,7 +152,7 @@ function ExperimentInstanceComponent({ | |||
| </div> | |||
| </div> | |||
| {experimentInList.map((item, index) => ( | |||
| {experimentInsList.map((item, index) => ( | |||
| <div | |||
| key={item.id} | |||
| className={classNames(styles.tableExpandBox, styles.tableExpandBoxContent)} | |||
| @@ -244,7 +244,7 @@ function ExperimentInstanceComponent({ | |||
| </div> | |||
| </div> | |||
| ))} | |||
| {experimentInsTotal > experimentInList.length ? ( | |||
| {experimentInsTotal > experimentInsList.length ? ( | |||
| <div className={styles.loadMoreBox}> | |||
| <Button type="link" onClick={onLoadMore}> | |||
| 更多 | |||
| @@ -1,11 +1,10 @@ | |||
| import ParameterInput from '@/components/ParameterInput'; | |||
| import FormInfo from '@/components/FormInfo'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { PipelineNodeModelSerialize } from '@/types'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { Form, Select } from 'antd'; | |||
| import styles from './index.less'; | |||
| const { TextArea } = Input; | |||
| type ExperimentParameterProps = { | |||
| nodeData: PipelineNodeModelSerialize; | |||
| @@ -64,7 +63,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| }, | |||
| ]} | |||
| > | |||
| <Input disabled /> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="任务ID" | |||
| @@ -76,7 +75,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| }, | |||
| ]} | |||
| > | |||
| <Input disabled /> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| @@ -94,14 +93,14 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| }, | |||
| ]} | |||
| > | |||
| <Input disabled /> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="工作目录" name="working_directory"> | |||
| <Input disabled /> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="启动命令" name="command"> | |||
| <TextArea disabled /> | |||
| <FormInfo multiline /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="资源规格" | |||
| @@ -123,14 +122,14 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| /> | |||
| </Form.Item> | |||
| <Form.Item label="挂载路径" name="mount_path"> | |||
| <Input disabled /> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="环境变量" name="env_variables"> | |||
| <TextArea disabled /> | |||
| <FormInfo multiline /> | |||
| </Form.Item> | |||
| {controlStrategyList.map((item) => ( | |||
| <Form.Item key={item.key} name={['control_strategy', item.key]} label={item.value.label}> | |||
| <ParameterInput disabled /> | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| ))} | |||
| <div className={styles['experiment-parameter__title']}> | |||
| @@ -149,7 +148,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| {item.value.type === 'select' ? ( | |||
| <ParameterSelect disabled /> | |||
| ) : ( | |||
| <ParameterInput disabled /> | |||
| <FormInfo valuePropName="showValue" /> | |||
| )} | |||
| </Form.Item> | |||
| ))} | |||
| @@ -166,7 +165,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| label={item.value.label + '(' + item.key + ')'} | |||
| rules={[{ required: item.value.require ? true : false }]} | |||
| > | |||
| <ParameterInput disabled /> | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| ))} | |||
| </Form> | |||
| @@ -537,7 +537,7 @@ function Experiment() { | |||
| expandable={{ | |||
| expandedRowRender: (record) => ( | |||
| <ExperimentInstance | |||
| experimentInList={experimentInList} | |||
| experimentInsList={experimentInList} | |||
| experimentInsTotal={experimentInsTotal} | |||
| onClickInstance={(item) => gotoInstanceInfo(item, record)} | |||
| onClickTensorBoard={handleTensorboard} | |||
| @@ -15,7 +15,7 @@ | |||
| } | |||
| } | |||
| &__desc { | |||
| margin-bottom: 20px; | |||
| margin-bottom: 15px; | |||
| padding: 4px 8px; | |||
| color: @text-color-tertiary; | |||
| font-size: 13px; | |||
| @@ -12,7 +12,7 @@ | |||
| .ant-table-thead { | |||
| .ant-table-cell { | |||
| background-color: rgb(247, 247, 247); | |||
| border-color: @border-color-base !important; | |||
| border-color: @border-color !important; | |||
| } | |||
| } | |||
| .ant-table-tbody { | |||
| @@ -23,7 +23,7 @@ | |||
| width: 100%; | |||
| height: 60px; | |||
| padding: 0 15px; | |||
| border-bottom: 1px solid @border-color-base; | |||
| border-bottom: 1px solid @border-color; | |||
| } | |||
| &__iframe { | |||
| @@ -71,6 +71,7 @@ export const Primary: Story = { | |||
| /** 通过 `openAntdModal` 函数打开 */ | |||
| export const OpenByFunction: Story = { | |||
| name: '通过函数的方式打开', | |||
| render: function Render(args) { | |||
| const handleClick = () => { | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| @@ -0,0 +1,124 @@ | |||
| import FormInfo from '@/components/FormInfo'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { Form, Input, Select, Typography } from 'antd'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/FormInfo', | |||
| component: FormInfo, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // 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: { onClick: fn() }, | |||
| } satisfies Meta<typeof FormInfo>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const InForm: Story = { | |||
| render: () => { | |||
| return ( | |||
| <Form | |||
| name="form" | |||
| style={{ width: 300 }} | |||
| labelCol={{ flex: '100px' }} | |||
| initialValues={{ | |||
| text: '文本', | |||
| large_text: | |||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||
| multiline_text: `多行文本\n超长文本超长文本超长文本超长文本\n 多行文本`, | |||
| object_text: { | |||
| value: 1, | |||
| showValue: '对象文本', | |||
| }, | |||
| input_text: | |||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||
| antd_select: 1, | |||
| select_text: 1, | |||
| select_large_text: 1, | |||
| }} | |||
| > | |||
| <Form.Item label="文本" name="text"> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="超长文本" name="large_text"> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="多行文本" name="multiline_text"> | |||
| <FormInfo multiline /> | |||
| </Form.Item> | |||
| <Form.Item label="对象" name="object_text"> | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| <Form.Item label="无内容" name="empty_text"> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="Input" name="input_text"> | |||
| <Input disabled /> | |||
| </Form.Item> | |||
| <Form.Item label="Select" name="antd_select"> | |||
| <Select | |||
| disabled | |||
| options={[ | |||
| { | |||
| label: | |||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||
| value: 1, | |||
| }, | |||
| ]} | |||
| /> | |||
| </Form.Item> | |||
| <Form.Item label="Select" name="select_text"> | |||
| <Select | |||
| labelRender={(props) => { | |||
| return ( | |||
| <div style={{ width: '100%', lineHeight: 'normal' }}> | |||
| <Typography.Text ellipsis={{ tooltip: props.label }} style={{ margin: 0 }}> | |||
| {props.label} | |||
| </Typography.Text> | |||
| </div> | |||
| ); | |||
| }} | |||
| disabled | |||
| options={[ | |||
| { | |||
| label: '选择文本', | |||
| value: 1, | |||
| }, | |||
| ]} | |||
| /> | |||
| </Form.Item> | |||
| <Form.Item label="Long Select" name="select_large_text"> | |||
| <Select | |||
| labelRender={(props) => { | |||
| return ( | |||
| <div style={{ width: '100%', lineHeight: 'normal' }}> | |||
| <Typography.Text ellipsis={{ tooltip: props.label }} style={{ margin: 0 }}> | |||
| {props.label} | |||
| </Typography.Text> | |||
| </div> | |||
| ); | |||
| }} | |||
| disabled | |||
| options={[ | |||
| { | |||
| label: | |||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||
| value: 1, | |||
| }, | |||
| ]} | |||
| /> | |||
| </Form.Item> | |||
| </Form> | |||
| ); | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,45 @@ | |||
| import { Meta, Title, Subtitle, Description, Primary, Controls, Stories } from '@storybook/blocks'; | |||
| import * as KFModalStories from "./KFModal.stories" | |||
| <Meta of={KFModalStories} /> | |||
| <Title /> | |||
| <Subtitle /> | |||
| <Description /> | |||
| ## Usage | |||
| 为了风格统一,应用中的其它 Modal 应该使用 **KFModal** 进行封装,例如 **CodeSelectorModal** | |||
| ```ts | |||
| export interface CodeSelectorModalProps extends Omit<ModalProps, 'onOk'> { | |||
| onOk?: (params: CodeConfigData | undefined) => void; | |||
| } | |||
| function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||
| return ( | |||
| <KFModal | |||
| {...rest} | |||
| title="选择代码配置" | |||
| image={require('@/assets/img/modal-code-config.png')} | |||
| width={920} | |||
| footer={null} | |||
| destroyOnClose | |||
| > | |||
| <div>children</div> | |||
| </KFModal> | |||
| ); | |||
| } | |||
| export default CodeSelectorModal; | |||
| ``` | |||
| ## Primary | |||
| <Primary /> | |||
| <Controls /> | |||
| <Stories /> | |||
| @@ -3,7 +3,7 @@ import KFModal from '@/components/KFModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { expect, fn, screen, userEvent, within } from '@storybook/test'; | |||
| import { Button } from 'antd'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| @@ -15,7 +15,7 @@ const meta = { | |||
| layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| tags: ['!autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| @@ -38,6 +38,7 @@ export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| /** 作为子组件时,通过 `open` 属性打开或关闭 */ | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '创建实验', | |||
| @@ -69,10 +70,17 @@ export const Primary: Story = { | |||
| </> | |||
| ); | |||
| }, | |||
| play: async ({ canvasElement }) => { | |||
| const canvas = within(canvasElement); | |||
| await userEvent.click(canvas.getByRole('button', { name: /打开 KFModal/i })); | |||
| const dialog = await screen.findByRole('dialog'); | |||
| await expect(dialog).toBeInTheDocument(); | |||
| }, | |||
| }; | |||
| /** 通过 `openAntdModal` 函数打开 */ | |||
| /** 推荐通过 `openAntdModal` 函数打开 */ | |||
| export const OpenByFunction: Story = { | |||
| name: '通过函数的方式打开', | |||
| render: function Render() { | |||
| const handleClick = () => { | |||
| const { close } = openAntdModal(KFModal, { | |||
| @@ -182,6 +182,7 @@ export const Mirror: Story = { | |||
| /** 通过 `openAntdModal` 函数打开 */ | |||
| export const OpenByFunction: Story = { | |||
| name: '通过函数的方式打开', | |||
| args: { | |||
| type: ResourceSelectorType.Mirror, | |||
| }, | |||
| @@ -0,0 +1,77 @@ | |||
| import { Meta, ColorPalette, ColorItem } from '@storybook/blocks'; | |||
| <Meta title="Documentation/Colors" /> | |||
| # Colors | |||
| <ColorPalette> | |||
| <ColorItem | |||
| title="theme.color.primary" | |||
| subtitle="主题色" | |||
| colors={{ "primary-color": '#1664ff' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.primary-hover" | |||
| subtitle="主题色:hover" | |||
| colors={{ "primary-color-hover": "#69b1ff" }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.underline" | |||
| subtitle="链接的下划线" | |||
| colors={{ "underline-color": "#5d93ff" }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.success" | |||
| subtitle="成功色" | |||
| colors={{ "success-color": '#6ac21d' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.error" | |||
| subtitle="失败色" | |||
| colors={{ "error-color": '#c73131' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.warning" | |||
| subtitle="警告色" | |||
| colors={{ "warning-color": '#f98e1b' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.warning" | |||
| subtitle="终止" | |||
| colors={{ "abort-color": '#8a8a8a' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.warning" | |||
| subtitle="等待运行" | |||
| colors={{ "pending-color": '#ecb934' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.background" | |||
| subtitle="背景色" | |||
| colors={{ | |||
| "background-color": '#f9fafb', | |||
| "sider-background-color": '#f2f5f7' | |||
| }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.font" | |||
| subtitle="字体颜色" | |||
| colors={{ | |||
| "text-color": '#1d1d20', | |||
| "text-color-secondary": '#575757', | |||
| "text-color-tertiary": '#8a8a8a', | |||
| "text-disabled-color": 'rgba(0, 0, 0, 0.25)', | |||
| }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.placeholder" | |||
| subtitle="placeholder 颜色" | |||
| colors={{ "text-placeholder-color": 'rgba(0, 0, 0, 0.25)' }} | |||
| /> | |||
| <ColorItem | |||
| title="theme.color.border" | |||
| subtitle="边框色" | |||
| colors={{ "border-color": '#eaeaea' }} | |||
| /> | |||
| </ColorPalette> | |||
| @@ -0,0 +1,25 @@ | |||
| import { Meta } from '@storybook/blocks'; | |||
| <Meta title="Documentation/Git Commit" /> | |||
| # Git 提交规范 | |||
| 遵循 [Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines) 规范: | |||
| - feat: 增加新功能 | |||
| - fix: 修复 bug | |||
| - build: 更改构建系统或外部依赖(例如:gulp, broccoli, npm) | |||
| - ci: 更改CI配置文件和脚本(例如:Travis, Circle, BrowserStack, SauceLabs) | |||
| - docs: 更改文档 | |||
| - perf: 提高性能的修改 | |||
| - refactor: 重构,但是即不是新增功能,也不是修复 bug | |||
| - style: 修改格式或样式,但是不会影响代码的运行(例如:删除空白、添加分号等) | |||
| - test: 添加测试 | |||
| - chore: 其它不修改 src 或测试文件的更改 | |||
| 例如 | |||
| ```sh | |||
| $ git commit -m "feat: 集成 Storybook 框架" | |||
| ``` | |||
| @@ -1,4 +1,4 @@ | |||
| import { Meta, Controls } from '@storybook/blocks'; | |||
| import { Meta } from '@storybook/blocks'; | |||
| <Meta title="Documentation/Less" /> | |||
| @@ -148,7 +148,7 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | |||
| ``` | |||
| ### 一些建议 | |||
| ### 一点建议 | |||
| 如果你陷入嵌套地狱,比如 | |||
| @@ -8,6 +8,7 @@ | |||
| @primary-color: #1664ff; // 主色调 | |||
| @primary-color-secondary: #4e89ff; | |||
| @primary-color-hover: #69b1ff; | |||
| @sider-background-color: #f2f5f7; // 侧边栏背景颜色 | |||
| @background-color: #f9fafb; // 页面背景颜色 | |||
| @text-color: #1d1d20; | |||
| @text-color-secondary: #575757; | |||
| @@ -20,19 +21,12 @@ | |||
| @abort-color: #8a8a8a; | |||
| @pending-color: #ecb934; | |||
| @underline-color: #5d93ff; | |||
| @border-color: #eaeaea; | |||
| @border-color-base: #eaeaea; | |||
| @border-color: rgba(22, 100, 255, 0.3); | |||
| @border-color-secondary: rgba(22, 100, 255, 0.1); | |||
| @background-color-primary: rgba(22, 100, 255, 0.03); | |||
| @background-color-gray: rgba(4, 3, 3, 0.06); | |||
| @link-hover-color: #69b1ff; | |||
| @heading-color: rgba(0, 0, 0, 0.85); | |||
| @input-icon-hover-color: rgba(0, 0, 0, 0.85); | |||
| @link-hover-color: #69b1ff; | |||
| @sider-background-color: #f2f5f7; | |||
| @workspace-background: linear-gradient( | |||
| 179.03deg, | |||
| rgba(138, 138, 138, 0.06) 0%, | |||
| @@ -21,6 +21,7 @@ | |||
| "incremental": true, // 通过读写磁盘上的文件来启用增量编译 | |||
| "noFallthroughCasesInSwitch": true, // 报告switch语句中的fallthrough案例错误 | |||
| "strictNullChecks": true, // 启用严格的null检查 | |||
| "importHelpers": true, | |||
| "baseUrl": "./", | |||
| "paths": { | |||
| "@/*": ["src/*"], | |||