From 4a630506e9e924dd5cf2ede830df1e2e2fefb435 Mon Sep 17 00:00:00 2001 From: Valerii Sidorenko Date: Wed, 20 Mar 2024 17:52:02 +0100 Subject: [PATCH] feat!: add uikit plugin instead of theme render props --- README.md | 43 ++++++++++++++++++++++++++++-- src/index.ts | 2 ++ src/plugins/index.ts | 15 +++++++++-- src/plugins/uikit/index.ts | 36 +++++++++++++++++++++++++ src/plugins/uikit/types.ts | 4 +++ src/render.test.ts | 17 ++++++++---- src/types.ts | 1 - src/utils/generateRenderContent.ts | 21 +-------------- 8 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 src/plugins/uikit/index.ts create mode 100644 src/plugins/uikit/types.ts diff --git a/README.md b/README.md index 7ec3b6c..599ac02 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ interface RenderParams { lang?: string; isMobile?: boolean; + // html attributes + htmlAttributes?: string; // header tag content // meta tags meta?: Meta[]; @@ -67,10 +69,10 @@ interface RenderParams { // content of body tag bodyContent?: { - // initial application theme if @gravity-ui/uikit is used - theme?: string; // class name for body tag className?: string; + // body attributes + attributes?: string; // body content before div tag with id root beforeRoot?: string; // innerHtml content of div tag with id root @@ -422,6 +424,43 @@ export interface LayoutOptions { } ``` +### @gravity-ui/uikit + +Adds body attributes. + +Usage: + +```js +import {createRenderFunction, createUikitPlugin} from '@gravity-ui/app-layout'; + +const renderLayout = createRenderFunction([createUikitPlugin()]); + +app.get((req, res) => { + res.send( + renderLayout({ + title: 'Home page', + pluginsOptions: { + uikit: { + theme: 'dark', + direction: 'ltr', + }, + }, + }), + ); +}); +``` + +Plugin options: + +```typescript +interface UikitPluginOptions { + theme: string; + direction?: 'ltr' | 'rtl'; +} +``` + +### Helpers + There is helper to create all plugins: ```js diff --git a/src/index.ts b/src/index.ts index b1d8a14..1198a30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export { createGoogleAnalyticsPlugin, createYandexMetrikaPlugin, createLayoutPlugin, + createUikitPlugin, createDefaultPlugins, } from './plugins/index.js'; @@ -31,3 +32,4 @@ export type { GoogleAnalyticsPluginOptions, } from './plugins/google-analytics/index.js'; export type {LayoutPluginOptions, Manifest} from './plugins/layout/index.js'; +export type {UikitPluginOptions} from './plugins/uikit/index.js'; diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 15ee434..0fd0d68 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,10 +1,21 @@ import {createGoogleAnalyticsPlugin} from './google-analytics/index.js'; import {createLayoutPlugin} from './layout/index.js'; import type {LayoutInitOptions} from './layout/index.js'; +import {createUikitPlugin} from './uikit/index.js'; import {createYandexMetrikaPlugin} from './yandex-metrika/index.js'; export function createDefaultPlugins({layout}: {layout: LayoutInitOptions}) { - return [createGoogleAnalyticsPlugin(), createYandexMetrikaPlugin(), createLayoutPlugin(layout)]; + return [ + createGoogleAnalyticsPlugin(), + createYandexMetrikaPlugin(), + createUikitPlugin(), + createLayoutPlugin(layout), + ]; } -export {createGoogleAnalyticsPlugin, createYandexMetrikaPlugin, createLayoutPlugin}; +export { + createGoogleAnalyticsPlugin, + createYandexMetrikaPlugin, + createLayoutPlugin, + createUikitPlugin, +}; diff --git a/src/plugins/uikit/index.ts b/src/plugins/uikit/index.ts new file mode 100644 index 0000000..1317e41 --- /dev/null +++ b/src/plugins/uikit/index.ts @@ -0,0 +1,36 @@ +import type {Plugin} from '../../types.js'; + +import type {UikitPluginOptions} from './types.js'; + +export type {UikitPluginOptions} from './types.js'; + +export function createUikitPlugin(): Plugin { + return { + name: 'uikit', + apply({options, renderContent}) { + if (!options) { + return; + } + + const {theme, direction} = options; + + const cls = renderContent.bodyContent.attributes.class; + const className = Array.from( + new Set([...(cls ? String(cls).split(/\s+/) : []), ...getRootClassName(theme)]), + ) + .filter(Boolean) + .join(' '); + renderContent.bodyContent.attributes.class = className; + + if (direction === 'rtl') { + renderContent.bodyContent.attributes.dir = 'rtl'; + } + }, + }; +} + +function getRootClassName(theme: string) { + const classes = ['g-root']; + classes.push(`g-root_theme_${theme}`); + return classes; +} diff --git a/src/plugins/uikit/types.ts b/src/plugins/uikit/types.ts new file mode 100644 index 0000000..302f7ef --- /dev/null +++ b/src/plugins/uikit/types.ts @@ -0,0 +1,4 @@ +export interface UikitPluginOptions { + theme: string; + direction?: 'ltr' | 'rtl'; +} diff --git a/src/render.test.ts b/src/render.test.ts index 3f6fb32..1e06563 100644 --- a/src/render.test.ts +++ b/src/render.test.ts @@ -1,3 +1,4 @@ +import {createUikitPlugin} from './plugins/index.js'; import {createRenderFunction} from './render.js'; import type {Plugin} from './types.js'; @@ -30,19 +31,25 @@ test('should render body classes', () => { }), ).toMatch(/\s*
\s*content\s*<\/div>\s*<\/body>/); expect( - createRenderFunction()({ + createRenderFunction([createUikitPlugin()])({ title: 'Foobar', - bodyContent: {root: 'content', theme: 'light'}, + bodyContent: {root: 'content'}, + pluginsOptions: { + uikit: {theme: 'light'}, + }, }), ).toMatch( /\s*
\s*content\s*<\/div>\s*<\/body>/, ); expect( - createRenderFunction()({ + createRenderFunction([createUikitPlugin()])({ title: 'Foobar', - bodyContent: {root: 'content', className: 'test', theme: 'light'}, + bodyContent: {root: 'content', className: 'test'}, + pluginsOptions: { + uikit: {theme: 'light'}, + }, }), ).toMatch( - /\s*
\s*content\s*<\/div>\s*<\/body>/, + /\s*
\s*content\s*<\/div>\s*<\/body>/, ); }); diff --git a/src/types.ts b/src/types.ts index 81489c3..a8d21df 100644 --- a/src/types.ts +++ b/src/types.ts @@ -92,7 +92,6 @@ export interface RenderParams extends Commo inlineScripts?: string[]; inlineStyleSheets?: string[]; bodyContent?: { - theme?: string; className?: string; attributes?: Attributes; beforeRoot?: string; diff --git a/src/utils/generateRenderContent.ts b/src/utils/generateRenderContent.ts index 97df9bf..b1c1080 100644 --- a/src/utils/generateRenderContent.ts +++ b/src/utils/generateRenderContent.ts @@ -11,17 +11,6 @@ import type { } from '../types.js'; import {getRenderHelpers, hasProperty} from '../utils.js'; -function getRootClassName(theme?: string) { - if (!theme) { - return []; - } - const classes = ['g-root']; - if (theme) { - classes.push(`g-root_theme_${theme}`); - } - return classes; -} - const defaultIcon: Icon = { type: 'image/png', sizes: '16x16', @@ -63,17 +52,9 @@ export function generateRenderContent( inlineScripts.unshift(`window.__DATA__ = ${htmlescape(params.data || {})};`); const content = params.bodyContent ?? {}; - - const {theme, className} = content; - const bodyClassName = Array.from( - new Set([...getRootClassName(theme), ...(className ? className.split(' ') : [])]), - ) - .filter(Boolean) - .join(' '); - const bodyContent: BodyContent = { attributes: { - class: bodyClassName, + class: content.className, ...content.attributes, }, root: content.root,