import { createContext, useContext } from 'react' import forEach from 'lodash/forEach' import { ComponentSize, useColors } from '../ConfigProvider' export const defaultSize: ComponentSize = 'default' export const sizeCtx = createContext(defaultSize) /** 获取控件所在表单的 size 信息 */ export function useFormSize(sizeFromProps: ComponentSize | undefined): ComponentSize { const sizeFromCtx = useContext(sizeCtx) return sizeFromProps ?? sizeFromCtx } /** 表单布局模式:水平、垂直、行内 */ export type LayoutType = 'horizontal' | 'vertical' | 'inline' /** label 内容的对齐方式 */ export type LabelAlign = 'left' | 'right' export const defaultLabelAlign: LabelAlign = 'left' /** * 可选的 label 文本颜色 * light: 浅色,适用于偏展示类表单 * deep: 深色,适用于偏操作类表单 */ export type LabelColor = 'light' | 'deep' export const defaultLabelColor: LabelColor = 'deep' export function useLabelColorMap(): Record { const colors = useColors() return { light: colors.grey9, deep: colors.grey10 } } /** * label 在垂直方向的对齐方式 * input: 与常规的表单输入组件()对齐,对应 `src/style/base.less` 中 `@icecream-form-input-height` 的高度 * text: 与文本对齐,对应文本的常规行高(`RadioGroup` / `CheckboxGroup` 等输入组件也是这个高度) */ export type LabelVerticalAlign = 'input' | 'text' export const defaultLabelVerticalAlign: LabelVerticalAlign = 'input' export const defaultLabelWidth = 'auto' export const defaultLabelGapForHorizontal = '28px' export const defaultLabelGapForInline = '8px' /** 表单 label 相关配置 */ export interface LabelOptions { /** * label 宽度,仅当表单 `layout: horizontal` 时生效 * @default 'auto' */ labelWidth?: string /** * label 与主体内容的间距,仅当表单 `layout: horizontal | inline` 时生效 * 一般来说间距不小于 16px,且是 4px 的整数倍 * @default '28px' (`layout: horizontal`) * @default '8px' (`layout: inline`) */ labelGap?: string /** * label 内容的对齐方式,仅当表单 `layout: horizontal` 时生效 * @default 'left' */ labelAlign?: LabelAlign /** * label 文本颜色 * @default 'deep' */ labelColor?: LabelColor /** * label 在垂直方向的对齐方式,设置该值使得 label 与右侧主体内容首行对齐,仅当表单 `layout: horizontal` 时生效 * @default 'input' */ labelVerticalAlign?: LabelVerticalAlign } export const labelOptionsCtx = createContext({}) /** 获取控件所在表单的 label 相关配置 */ export function useFormLabelOptions(labelOptionsFromProps?: LabelOptions): LabelOptions { const labelOptionsFromCtx = useContext(labelOptionsCtx) const labelOptions: LabelOptions = { ...labelOptionsFromCtx } forEach(labelOptionsFromProps, (v, k) => { // 值为 undefined 的项应视作没传,不去覆盖 ctx 中的对应项 if (v !== undefined) (labelOptions as any)[k] = v }) return labelOptions } /** 表单布局配置 */ export type Layout = { /** 表单布局模式 */ type: LayoutType } export function normalizeLayout(layout: Layout | LayoutType): Layout { return typeof layout === 'string' ? { type: layout } : layout } export const defaultLayout: Layout = { type: 'vertical' } export const layoutCtx = createContext(defaultLayout) // TODO 结合 ConfigProvider 里面的 size /** 获取控件所在表单的 layout 信息 */ export function useFormLayout(layoutFromProps: LayoutType | Layout | undefined) { const layoutFromCtx = useContext(layoutCtx) return layoutFromProps != null ? normalizeLayout(layoutFromProps) : layoutFromCtx } /** 表单项的校验状态:未校验、校验通过、校验失败 */ export type ValidateStatus = 'default' | 'success' | 'error' export const defaultValidateStatus: ValidateStatus = 'default' export type ValidateMessage = string | null | undefined export type ValidateResult = { status: ValidateStatus message: ValidateMessage } export const validateResultCtx = createContext({ status: defaultValidateStatus, message: undefined }) export const defaultVerticalGap = '24px' // TODO: 等 Form size 跟 ConfigProvider size 结合的事儿做完后, // 梳理下现有的其他 Form options,可以把部分 options 合并 context export const verticalGapCtx = createContext(defaultVerticalGap) /** 获取所在表单的垂直间距信息 */ export function useFormVerticalGap(verticalGapFromProps: string | undefined) { const verticalGapFromCtx = useContext(verticalGapCtx) return verticalGapFromProps != null ? verticalGapFromProps : verticalGapFromCtx } /** 用于输入控件获取所在表单的信息的 hook */ export function useFormInfo() { const size = useContext(sizeCtx) const layout = useContext(layoutCtx) const validateResult = useContext(validateResultCtx) return { size, layout, validateStatus: validateResult.status, validateMessage: validateResult.message } } /** Footer 内容的对齐方向(对 `layout: inline` 的 Form 不生效) */ export type FooterAlign = 'left' | 'right' /** 控制 Footer 内容默认对齐方向的 context,(icecream)内部使用 */ export const footerDefaultAlignCtx = createContext('left') export interface FooterCtxValue { submitting: boolean onCancel?: () => void } export const footerCtx = createContext({ submitting: false }) /** 消费 footer 相关的 context 内容 */ export function useFooterCtx() { const footerCtxValue = useContext(footerCtx) const footerDefaultAlign = useContext(footerDefaultAlignCtx) return { ...footerCtxValue, defaultAlign: footerDefaultAlign } }