Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Yakutoc opened this issue Feb 25, 2025 · 1 comment

Comments

@Yakutoc
Copy link
Collaborator

Yakutoc commented Feb 25, 2025

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)',
            },
        },
    }
});
@Yakutoc
Copy link
Collaborator Author

Yakutoc commented Feb 25, 2025

TODO:

  • нужны типы
  • нужна логика exclude
    • на уровне типов
    • на уровне variation, аля исключить ['warning', 'accent']
    • на уровне токенов, аля исключить у view.default - ['tokens.buttonBackgroundColor']

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant