Skip to content

[rnd][ infra]: Генерация/merge конфигураций компонентов #1796

@Yakutoc

Description

@Yakutoc

Goals

Гибкая система, что не мешает вносить изменения в конфигурации компонентов централизованно и управляемо.

Проблемы:

  • большое количество шаблонного кода в конфигурациях;
  • очень много вхождений для правок конфигураций;
  • бывает что теряются часть конфигураций;

Например:

Для Accordion view["default"] в пакетах на базе sdds-serv имеется абсолютно одинаковый набор свойств.

POC

Общая конфигурация компонент Button

import { buttonTokens as tokens } from '@salutejs/plasma-new-hope';

const generateFontConfig = (size = 'm', weight = 'normal') => {
    return {
        [tokens.buttonFontFamily]: `var(--plasma-typo-body-${size}-font-family)`,
        [tokens.buttonFontSize]: `var(--plasma-typo-body-${size}-font-size)`,
        [tokens.buttonFontStyle]: `var(--plasma-typo-body-${size}-font-style)`,
        [tokens.buttonFontWeight]: `var(--plasma-typo-body-${size}-${weight}-font-weight)`,
        [tokens.buttonLetterSpacing]: `var(--plasma-typo-body-${size}-letter-spacing)`,
        [tokens.buttonLineHeight]: `var(--plasma-typo-body-${size}-line-height)`,
    };
};

export const ButtonBaseConfig = {
    defaults: {
        view: 'default',
        focused: 'true',
        size: 'm',
    },
    variations: {
        view: {
            default: {
                [tokens.buttonColor]: 'var(--inverse-text-primary)',
                [tokens.buttonValueColor]: 'var(--inverse-text-secondary)',
                [tokens.buttonBackgroundColor]: 'var(--surface-solid-default)',
                [tokens.buttonColorHover]: 'var(--inverse-text-primary)',
                [tokens.buttonBackgroundColorHover]: 'var(--surface-solid-default-hover)',
                [tokens.buttonColorActive]: 'var(--inverse-text-primary)',
                [tokens.buttonBackgroundColorActive]: 'var(--surface-solid-default-active)',
                [tokens.buttonLoadingBackgroundColor]: `var(${tokens.buttonBackgroundColor})`,
            },
            accent: {
                [tokens.buttonColor]: 'var(--on-dark-text-primary)',
                [tokens.buttonValueColor]: 'var(--on-dark-text-secondary)',
                [tokens.buttonBackgroundColor]: 'var(--surface-accent)',
                [tokens.buttonColorHover]: 'var(--on-dark-text-primary)',
                [tokens.buttonBackgroundColorHover]: 'var(--surface-accent-hover)',
                [tokens.buttonColorActive]: 'var(--on-dark-text-primary)',
                [tokens.buttonBackgroundColorActive]: 'var(--surface-accent-active)',
                [tokens.buttonLoadingBackgroundColor]: 'var(--surface-solid-default-active)',
                [tokens.buttonLoadingBackgroundColor]: `var(${tokens.buttonBackgroundColor})`,
            },
        },
        size: {
            m: {
                [tokens.buttonHeight]: '3rem',
                [tokens.buttonWidth]: '11.25rem',
                [tokens.buttonPadding]: '1.25rem',
                [tokens.buttonRadius]: '0.75rem',
                [tokens.buttonSpinnerSize]: '1.375rem',
                [tokens.buttonSpinnerColor]: 'inherit',
                [tokens.buttonLeftContentMargin]: '0 0.375rem 0 -0.125rem',
                [tokens.buttonRightContentMargin]: '0 -0.125rem 0 0.375rem',
                [tokens.buttonValueMargin]: '0 0 0 0.25rem',
                ...generateFontConfig('m', 'bold'),
            },
            s: {
                [tokens.buttonHeight]: '2.5rem',
                [tokens.buttonWidth]: '11.25rem',
                [tokens.buttonPadding]: '1rem',
                [tokens.buttonRadius]: '0.625rem',
                [tokens.buttonSpinnerSize]: '1.375rem',
                [tokens.buttonSpinnerColor]: 'inherit',
                [tokens.buttonLeftContentMargin]: '0 0.25rem 0 -0.125rem',
                [tokens.buttonRightContentMargin]: '0 -0.125rem 0 0.25rem',
                [tokens.buttonValueMargin]: '0 0 0 0.25rem',
                ...generateFontConfig('m', 'bold'),
            },
        },
        focused: {
            true: {
                [tokens.buttonFocusColor]: 'var(--surface-accent)',
            },
        },
        stretching: {
            auto: {},
            filled: {},
            fixed: {},
        },
    },
};

Абстрактный класс компонента

class AbstractConfig {
    public variations = {};

    public defaults = {};

    getVariationsKeys(variation) {
        return Object.keys(this.variations[variation]);
    }

    getVariationProp(variations, path) {
        const [variation, prop] = path.split('.');

        if (!variations[variation] || !variations[variation][prop]) {
            return {};
        }

        return variations[variation][prop];
    }

    generateVariation(config) {
        const data = Object.values(tokens)
            .filter((key) => !!config[key])
            .reduce((acc, key) => {
                acc += `${key}: ${config[key]}; \n`;
                return acc;
            }, '');

        return css`
            ${data};
        `;
    }

    createVariation({ baseConfig, libConfig, variation }) {
        return Object.keys(baseConfig.variations[variation]).reduce((acc, key) => {
            const path = `${variation}.${key}`;

            return {
                ...acc,
                [key]: this.generateVariation({
                    ...this.getVariationProp(baseConfig.variations, path),
                    ...this.getVariationProp(libConfig.variations, path),
                }),
            };
        }, {});
    }
}

Класс компонента Button

export class ButtonBase extends AbstractConfig {
    constructor(baseConfig, libConfig) {
        super();
        this.defaults = { ...baseConfig.defaults, ...libConfig.defaults };

        // INFO: Merge configs
        this.variations = {
            view: this.createVariation({
                baseConfig,
                libConfig,
                variation: 'view',
            }),
            size: this.createVariation({
                baseConfig,
                libConfig,
                variation: 'size',
            }),
            focused: this.createVariation({
                baseConfig,
                libConfig,
                variation: 'focused',
            }),
        };
    }
}

Создание экземпляра конфигурации

import { buttonTokens as tokens } from '@salutejs/plasma-new-hope/styled-components';

import { ButtonBase } from '...';
import { ButtonBaseConfig } from '...';

export const config = new ButtonBase(ButtonBaseConfig, {
    defaults: {
        view: 'accent',
        focused: 'true',
        size: 's',
    },
    variations: {
        view: {
            default: {
                [tokens.buttonBackgroundColor]: 'var(--surface-accent)',
            },
        },
    }
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions