diff --git a/docs/ layout1.png b/docs/ layout1.png deleted file mode 100644 index 05f64183f..000000000 Binary files a/docs/ layout1.png and /dev/null differ diff --git a/docs/layout2.png b/docs/layout2.png deleted file mode 100644 index 901ab7370..000000000 Binary files a/docs/layout2.png and /dev/null differ diff --git a/docs/layout3.png b/docs/layout3.png deleted file mode 100644 index e17eb80d1..000000000 Binary files a/docs/layout3.png and /dev/null differ diff --git a/docs/layout4.png b/docs/layout4.png deleted file mode 100644 index 26b75234b..000000000 Binary files a/docs/layout4.png and /dev/null differ diff --git a/docs/layout5.png b/docs/layout5.png deleted file mode 100644 index a7de80329..000000000 Binary files a/docs/layout5.png and /dev/null differ diff --git a/index.d.ts b/index.d.ts index 0a4fda220..caa174aab 100644 --- a/index.d.ts +++ b/index.d.ts @@ -8,6 +8,7 @@ export { flatten, unflatten } from './types/src/admin-bro' export * from '@admin-bro/design-system' export * from './types/src/frontend/store/store' export * from './types/src/backend/utils/build-feature' +export * from './types/src/backend/utils/layout-element-parser' export * from './types/src/frontend/utils/overridable-component' export { default as Router } from './types/src/backend/router' diff --git a/package.json b/package.json index ed9073bb7..02233ec68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "admin-bro", - "version": "3.1.2", + "version": "3.2.0", "description": "Admin panel for apps written in node.js", "main": "index.js", "types": "index.d.ts", diff --git a/src/backend/actions/action.interface.ts b/src/backend/actions/action.interface.ts index a65795c8d..538a9608a 100644 --- a/src/backend/actions/action.interface.ts +++ b/src/backend/actions/action.interface.ts @@ -4,7 +4,7 @@ import ViewHelpers from '../utils/view-helpers' import BaseRecord from '../adapters/base-record' import BaseResource from '../adapters/base-resource' import ActionDecorator from '../decorators/action-decorator' -import { LayoutElement } from '../utils/layout-element-parser' +import { LayoutElement, LayoutElementFunction } from '../utils/layout-element-parser' import RecordJSON from '../decorators/record-json.interface' import { NoticeMessage } from '../../frontend/store/with-notice' import { TranslateFunctions } from '../../utils/translate-functions.factory' @@ -583,6 +583,7 @@ export default interface Action { * * This is an example of defining a layout * + * ``` * const layout = [{ width: 1 / 2 }, [ * ['@H3', { children: 'Company data' }], * 'companyName', @@ -596,13 +597,16 @@ export default interface Action { * ]], * ], * ] + * ``` * - * Alternatively you can pass a function taking {@link CurrentAdmin} as an argument. - * This will allow you to show/hide given property for restricted users. + * Alternatively you can pass a {@link LayoutElementFunction function} taking + * {@link CurrentAdmin} as an argument. This will allow you to show/hide + * given property for restricted users. * * To see entire documentation and more examples visit {@link LayoutElement} * * @see LayoutElement + * @see LayoutElementFunction */ - layout?: ((currentAdmin?: CurrentAdmin) => Array) | Array; + layout?: LayoutElementFunction | Array; } diff --git a/src/backend/utils/layout-element-parser.ts b/src/backend/utils/layout-element-parser.ts index 88d88f5a3..614268fd8 100644 --- a/src/backend/utils/layout-element-parser.ts +++ b/src/backend/utils/layout-element-parser.ts @@ -1,148 +1,8 @@ /* eslint-disable max-len */ import { BoxProps, HeaderProps, TextProps, BadgeProps, ButtonProps, LinkProps, LabelProps, IconProps } from '@admin-bro/design-system' import { PropsWithChildren } from 'react' +import { CurrentAdmin } from '../../current-admin.interface' -/** - * @typedef {String | Array} LayoutElement - * - * @description - * {@link LayoutElement} is used to change the default layout of edit and show {@link Action actions}. - * You define the layout as an {@link Array} and AdminBro renders it with React components. - * - * You don't have to know React to create usable Layout for you actions but be sure - * to take a look at the possible **Props** which can be used to style the components. - * The most often used props are {@link BoxProps}, because {@link Box} is the default wrapper. - * - * ### Available values for a {@link LayoutElement} type - * - * To {@link Action#layout } you have to pass an {@link Array}. Where each - * {@link LayoutElement} could have a different type defining its position and purpose. - * - * ### Type definition - * - * Those are available types of - * - * | Type | Purpose | Example | - * |---------------|--------------------------------------------------------------|------------------| - * | string | It will be changed to the property in vertical layout | `layout: ['name']` | - * | Array | It will be changed to the properties in vertical layout | `layout: [['name', 'surname']]` | - * | [string, {@link BoxProps}] | property wrapped by {@link Box} component with {@link BoxProps} | `layout: [['name', {width: 1/2}]]` | - * | [{@link BoxProps}, Array] | Creates a Box and nest all the child LayoutElements inside. | `layout: [[{width: 1/2}, ['name', 'surname']]]` | - * | Array | For grouping LayoutElements inside a wrapper | `layout: [['name', {mt: 'xl'}], ['surname', , {ml: 'xl'}]]` | - * | [@ComponentName, PropsWithChildren] | if you precede first item with "@" it will create component of this name | `layout: [['@Header', {children: 'User Data'}]]` | - * - * ### Examples - * - * Let say you have following properties in your database: `companyName`, `email`, `address` and `companySize` - * - * 1. The simplest horizontal layout: - * - * ``` - * const layout = [ - * 'companyName', - * 'email', - * 'address', - * 'companySize', - * ] - * ``` - * - * generates: - * - * - * - * 2. Now Wrap everything with a {@link Box} of `2/3` max width and horizontal margin (mx) - * set to auto. This will center all inputs - * - * ``` - * const layout = [ - * [{ width: 2 / 3, mx: 'auto' }, [ - * 'companyName', - * 'email', - * 'address', - * 'companySize', - * ]], - * ] - * ``` - * - * > Hint: you can also pass an array to define how it will behave in a different responsive breakpoints. - * - * generates: - * - * - * - * 3. Add headers between sections - * - * ``` - * const layout = [ - * [{ width: 2 / 3, mx: 'auto' }, [ - * ['@H3', { children: 'Company data' }], - * 'companyName', - * 'companySize', - * ['@H3', { children: 'Contact Info' }], - * 'email', - * 'address', - * ]], - * ] - * ``` - * - * > To inject content inside the given Component pass children props to it. - * - * generates: - * - * - * - * 4. Make email and address 50% width - * - * > We will wrap them with a Box (default component) which is a flex. Then we will have to wrap also each of them - * with extra box to define paddings. - * - * I will also align to left top section that by removing `{ mx: auto }` and changing width to `1 / 2`. - * - * const layout = [{ width: 1 / 2 }, [ - * ['@H3', { children: 'Company data' }], - * 'companyName', - * 'companySize', - * ]], - * [ - * ['@H3', { children: 'Contact Info' }], - * [{ flexDirection: 'row', flex: true }, [ - * ['email', { pr: 'default', flexGrow: 1 }], - * ['address', { flexGrow: 1 }], - * ]], - * ], - * ] - * - * generates: - * - * - * - * 5. Lastly, take a look at the example with a function instead of {@link LayoutElement}. - * - * ``` - * const layout = currentAdmin => ([ - * ['@MessageBox', { - * message: `Welcome ${currentAdmin && currentAdmin.email}`, - * children: 'On this page yo can do whatever you like', - * variant: 'info', - * mb: 'xxl', - * }], - * [ - * 'companyName', - * 'companySize', - * 'email', - * 'address', - * ], - * ]) - * ``` - * - * * generates following Show page: - * - * - * - * @memberof Action - */ - -// spacer for jsdoc export type LayoutElement = string | Array | @@ -163,6 +23,15 @@ export type LayoutElement = ['@Icon', PropsWithChildren] | [string, PropsWithChildren] +/** + * Function returning Array used by {@link Action#layout} + * + * @return {Array} + * @memberof Action + * @alias LayoutElementFunction + */ +export type LayoutElementFunction = (currentAdmin?: CurrentAdmin) => Array + /** * It is generated from {@link Array} passed in {@link Action#layout} * @@ -268,3 +137,145 @@ const layoutElementParser = (layoutElement: LayoutElement): ParsedLayoutElement } export default layoutElementParser + + +/** + * {@link LayoutElement} is used to change the default layout of edit and show {@link Action actions}. + * You define the layout as an {@link Array} and AdminBro renders it with React components. + * + * You don't have to know React to create usable Layout for you actions but be sure + * to take a look at the possible **Props** which can be used to style the components. + * The most often used props are {@link BoxProps}, because {@link Box} is the default wrapper. + * + * ### Available values for a {@link LayoutElement} type + * + * To {@link Action#layout } you have to pass an {@link Array}. Where each + * {@link LayoutElement} could have a different type defining its position and purpose. + * + * ### Type definition + * + * Those are available types for {@link LayoutElement} + * + * | Type | Purpose | Example | + * |---------------|--------------------------------------------------------------|------------------| + * | string | It will be changed to the property in vertical layout | `layout: ['name']` | + * | {@link Array} | It will be changed to the properties in vertical layout | `layout: [['name', 'surname']]` | + * | [string, {@link BoxProps}] | property wrapped by {@link Box} component with {@link BoxProps} | `layout: [['name', {width: 1/2}]]` | + * | [{@link BoxProps}, {@link Array}] | Creates a Box and nest all the child LayoutElements inside. | `layout: [[{width: 1/2}, ['name', 'surname']]]` | + * | {@link Array} | For grouping LayoutElements inside a wrapper | `layout: [['name', {mt: 'xl'}], ['surname', , {ml: 'xl'}]]` | + * | [@ComponentName, PropsWithChildren] | if you precede first item with "@" it will create component of this name | `layout: [['@Header', {children: 'User Data'}]]` | + * + * ### Examples + * + * Let say you have following properties in your database: `companyName`, `email`, `address` and `companySize` + * + * #### 1. The simplest horizontal layout: + * + * ``` + * const layout = [ + * 'companyName', + * 'email', + * 'address', + * 'companySize', + * ] + * ``` + * + * generates: + * + * + * + * #### 2. Now Wrap everything with a {@link Box} of `2/3` max width and horizontal margin (mx) set to auto. This will center all inputs + * + * ``` + * const layout = [ + * [{ width: 2 / 3, mx: 'auto' }, [ + * 'companyName', + * 'email', + * 'address', + * 'companySize', + * ]], + * ] + * ``` + * + * generates: + * + * + * + * > Hint: you can also pass an array to `width` to define how it will behave in a different responsive breakpoints. + * + * #### 3. Add headers between sections + * + * ``` + * const layout = [ + * [{ width: 2 / 3, mx: 'auto' }, [ + * ['@H3', { children: 'Company data' }], + * 'companyName', + * 'companySize', + * ['@H3', { children: 'Contact Info' }], + * 'email', + * 'address', + * ]], + * ] + * ``` + * + * generates: + * + * + * + * > To inject content inside the given Component pass children props to it. + * + * #### 4. Make email and address 50% width + * + * We will wrap them with a {@link Box} (default component) which is a flex. + * Then we will have to wrap also each of them with extra box to define paddings. + * + * I will also align to left top section that by removing `{ mx: auto }` and changing width to `1 / 2`. + * + * ``` + * const layout = [{ width: 1 / 2 }, [ + * ['@H3', { children: 'Company data' }], + * 'companyName', + * 'companySize', + * ]], + * [ + * ['@H3', { children: 'Contact Info' }], + * [{ flexDirection: 'row', flex: true }, [ + * ['email', { pr: 'default', flexGrow: 1 }], + * ['address', { flexGrow: 1 }], + * ]], + * ], + * ] + * ``` + * + * generates: + * + * + * + * #### 5. Lastly, take a look at the example with a function instead of {@link LayoutElement}. + * + * ``` + * const layout = currentAdmin => ([ + * ['@MessageBox', { + * message: `Welcome ${currentAdmin && currentAdmin.email}`, + * children: 'On this page yo can do whatever you like', + * variant: 'info', + * mb: 'xxl', + * }], + * [ + * 'companyName', + * 'companySize', + * 'email', + * 'address', + * ], + * ]) + * ``` + * + * Generates following **Show** page: + * + * + * + * @name LayoutElement + * @typedef {String | Array} LayoutElement + * @memberof Action + * @alias LayoutElement + */