Изменить цвет строки по кнопке

В данном примере создаётся новая колонка таблицы грида. В новую колонку добавляется элемент, нажатие на который открывает диалог выбора цвета. При изменении цвета в диалоге, будет изменяться фон строки, в которой находится ячейка.

Данный пример демонстрирует только взаимодействие новой колонки, её ячеек и фона строки, в которой они находятся

Начальный код расширения должен импортировать прямо или косвенно все остальные строки, чтобы затем объединить код в один пакет.

extensionManager.registerExtension({ (1)
    name: "BackgroundRows",
    version: "5.5.17",
    initialize() {
        app.folderPluginProvider.addFactory(new BackgroundRowsPluginFactory());
    },
})
1 Регистрация расширения позволяет корректно установить все обработчики событий, сервисы и прочие сущности web-приложения.
Создание кнопки, открывающей диалог выбора цвета:
export interface IBackgroundRowsButton extends PropsWithChildren<ICompositionPluginProps<"TableCell", ICellData, $BackgroundRows>> {
}

export function BackgroundRowsButton (props: IBackgroundRowsButton) { (1)
    const { services: { backgroundRows }, data: { row } } = props.composition;

    const [ color, setColor ] = useState( backgroundRows.$rows.getState()[row.entityId] ?? "#ffffff" ); (2)

    const changeColor = (ev: ChangeEvent<HTMLInputElement>) => {
        backgroundRows.setBackground({ id: row.entityId, color: ev.target.value }); (3)
        setColor(ev.target.value); (4)
    };

    const click = (ev: React.MouseEvent) => {
        ev.stopPropagation(); (5)
    };

    if (!getRowLoaded(row).loaded) { (6)
        return <></>
    }

    return (
        <input
            className="background-rows__button"
            type="color"
            value={color}
            onClick={click}
            onChange={changeColor} />
    )
};
1 Предоставляет возможность отобразить палитру цветов
2 Исходный цвет: белый.
3 Обновляем хранилище.
4 Обновляем локальное состояние input с type=color.
5 Прекращает дальнейшую передачу текущего события.
6 Если строка ещё не загружена, то ничего не показываем.
Создаём сервис, который будет хранить и обновлять цвет строки:
export class BackgroundRowsService implements IBackgroundRowsService {
    $rows: Store<IBackgroundRows>;
    setBackground: Event<IBackgroundRow>;

    constructor() {
        const domain = createDomain("BackgroundRows"); (1)

        this.setBackground = domain.event("setBackground");
        this.$rows = domain.store({}, { name: "$rows" })
            .on(this.setBackground, (store, background) => ({...store, [background.id]: background.color }));
    }
}
1 Инициализируем сущности необходимые для хранения и обновления хранилища.
Добавляем интерфейс сервиса:
export interface IBackgroundRows {
    [id: string]: string;
}

export interface IBackgroundRow {
    id: string;
    color: string;
}

export interface IBackgroundRowsService { (1)
    readonly $rows: Store<IBackgroundRows>;
    setBackground: Event<IBackgroundRow>;
}
1 Интерфейс сервиса.
Инициализируем сущности, необходимые для хранения и обновления хранилища:
export class BackgroundRowsService implements IBackgroundRowsService {
    $rows: Store<IBackgroundRows>;
    setBackground: Event<IBackgroundRow>;

    constructor() {
        const domain = createDomain("BackgroundRows");

        this.setBackground = domain.event("setBackground");
        this.$rows = domain.store({}, { name: "$rows" })
            .on(this.setBackground, (store, background) => ({...store, [background.id]: background.color }));
    }
}
Подключаем плагины грида:
export class BackgroundRowsPluginFactory implements IFolderPluginFactory {
    id: string = "BackgroundRowsPluginFactory";

    getDataLoadingPlugins(): IFolderDataLoadingPlugin[] {
        return [
            new BackgroundRowsResponseResolver()
        ]
    }

    getTablePlugins(): ITablePlugins[] {
        return [
            BackgroundRowsPlugins
        ];
    }
}
Добавляем новую колонку для элемента переключения цвета:
export class BackgroundRowsResponseResolver implements IFolderDataLoadingPlugin {
    id: string = "BackgroundRowsResponseResolver";
    description: string = "Добавляет новую колонку.";
    order: PluginOrder = PluginOrder.Normal;

    async resolveResponse(data: ITableData, response: GenModels.GridViewModelEx): Promise<void | ResponseResolveResult> { (1)
        if (data.columns.length && !data.columns.find(x => x.id == BackgroundRowsColumnId)) { (2)

            const backgroundRowsColumn = {
                id: BackgroundRowsColumnId, (3)
                name: "Фон строки", (4)
            } as IColumn;

            data.columns.push(backgroundRowsColumn);
        }
    }
}
1 Вызывается после каждого ответа загрузки данных таблицы.
2 Сделаем проверку на существование столбцов в таблице, а также проверим не был ли добавлен столбец, который мы хотим создать.
3 Уникальный идентификатор столбца.
4 Отображаемое название столбца.
Настраиваем элемент в ячейке:
export const BackgroundRowsColumnId = "backgroundRows";
export const BackgroundRowsFeature = "BackgroundRowsFeature";

export const BackgroundRowsServiceProvider: TablePlugins.ServiceProvider<$BackgroundRows> = {
    name: "BackgroundRowsServiceProvider",
    description: "Добавляет сервис $BackgroundRows.",
    feature: BackgroundRowsFeature,
    composition: TableCompositionNames.Root,
    addServices: (composition) => {
        if (!composition.services.backgroundRows) { (1)
            composition.services.backgroundRows = new BackgroundRowsService();
        }
    }
};

export const BackgroundRowsCellButtonPlugin: TablePlugins.Cell.Component<$BackgroundRows> = {
    name: "BackgroundRowsCellButtonPlugin",
    description: "Отображает кнопку изменения фона строки.",
    feature: BackgroundRowsFeature,
    composition: TableCompositionNames.TableCell,
    shouldRender: (composition) => composition.data.column.id == BackgroundRowsColumnId, (2)
    component: BackgroundRowsButton (3)
};

export const BackgroundRowsMountEffect: TablePlugins.Row.MountEffect<$BackgroundRows> = {
    name: "BackgroundRowsMountEffect",
    description: "Обновляет композицию при изменении $rows",
    feature: BackgroundRowsFeature,
    composition: TableCompositionNames.TableRow,
    compositionDidMount: (composition) => { (4)
        const { backgroundRows } = composition.services;

        const optimalUpdate = throttle({ source: backgroundRows.$rows, timeout: 100 }); (5)

        const update = optimalUpdate.watch(() => composition.update()); (6)
        return () => update.unsubscribe(); (7)
    }
};

export const BackgroundRowDecorator: TablePlugins.Row.Decorator<$BackgroundRows> = {
    name: "BackgroundRowDecorator",
    description: "Изменяет фон строки.",
    feature: BackgroundRowsFeature,
    composition: TableCompositionNames.TableRow,
    jsxDecorator: (node, composition) => { (8)
        const { services, data } = composition;
        const backgroundRows = services.backgroundRows.$rows.getState(); (9)

        return decorate(node, { style: { background: backgroundRows[data.row.entityId] }}); (10)
    }
};

export const BackgroundRowsPlugins: ITablePlugins = {
    serviceProviders: [ BackgroundRowsServiceProvider ],
    row: {
        mountEffects: [ BackgroundRowsMountEffect ],
        containerDecorators: [ BackgroundRowDecorator ]
    },
    cell: {
        content: [ BackgroundRowsCellButtonPlugin ]
    }
};
1 Поскольку addServices вызывается при каждой отрисовке, сделаем проверку на существование сервиса, который хотим добавить.
2 Компонент будет отображён, если условие правдиво.
3 Компонент, который будет отображён в ячейке добавленной колонки.
4 Вызывается при монтировании компоненты.
5 Поскольку хранилище backgroundRow.$rows может меняться достаточно быстро (зависит от скорости изменения цвета в палитре), необходимо установить ограничение обновления композиции.

В данном случае, optimalUpdate будет срабатывать не чаще, чем раз в 100 мс для оптимизации браузера.

6 Когда сработает событие, композиция таблицы будет обновлена.
7 Отписка от события, когда компонент будет размонтирован.
8 Метод, с помощью которого появляется возможность декорирования ReactNode.
9 Получаем хранилище, которое содержит информацию о цвете фона строк.
10 В данном методе вторым аргументом является объект свойств ReactNode, как в React.createElement.

В данном случае, если хранилище содержит информацию о фоне строки, она будет применена, в противном случае стиль не применится.

Проверка примера

  1. Откройте папку с таблицей. В колонке "Фон строки" должен отображаться созданный элемент.

  2. Нажмите на любой элемент в колонке и измените цвет.