diff --git a/packages/demo/src/schemas/demoLists.js b/packages/demo/src/schemas/demoLists.js index a9339250..a83f3926 100644 --- a/packages/demo/src/schemas/demoLists.js +++ b/packages/demo/src/schemas/demoLists.js @@ -50,6 +50,16 @@ const schemaLists = createOrderedMap({ 'end_check': true, }, ], + listActionLabels: { + en: { + add: 'New event', + remove: 'Remove event', + }, + de: { + add: 'Neue Veranstaltung', + remove: 'Lösche Veranstaltung', + }, + }, view: { sizeXs: 12, sizeMd: 12, diff --git a/packages/dictionary/src/de/labels.js b/packages/dictionary/src/de/labels.js index f6972056..90c2d76c 100644 --- a/packages/dictionary/src/de/labels.js +++ b/packages/dictionary/src/de/labels.js @@ -9,8 +9,14 @@ export const labels = { 'add-entry': 'Neuer Eintrag', 'entry': 'Eintrag', 'remove-entry': 'Entferne Eintrag', - 'add-item': 'Neues Element', - 'remove-item': 'Lösche Element', + 'add-item': (context, locale) => + context?.getIn(['actionLabels', locale, 'add']) ? + context?.getIn(['actionLabels', locale, 'add']) : + 'Neues Element', + 'remove-item': (context, locale) => + context?.getIn(['actionLabels', locale, 'remove']) ? + context?.getIn(['actionLabels', locale, 'remove']) : + 'Lösche Element', 'add-row': 'Neue Zeile', 'remove-row': 'Lösche Zeile', 'remove-rows': 'Lösche Zeilen', diff --git a/packages/dictionary/src/en/labels.js b/packages/dictionary/src/en/labels.js index 21e391e2..fc4ed013 100644 --- a/packages/dictionary/src/en/labels.js +++ b/packages/dictionary/src/en/labels.js @@ -9,8 +9,14 @@ export const labels = { 'add-entry': 'Add entry', 'entry': 'Entry', 'remove-entry': 'Remove entry', - 'add-item': 'Add item', - 'remove-item': 'Remove item', + 'add-item': (context, locale) => + context?.getIn(['actionLabels', locale, 'add']) ? + context?.getIn(['actionLabels', locale, 'add']) : + 'Add item', + 'remove-item': (context, locale) => + context?.getIn(['actionLabels', locale, 'remove']) ? + context?.getIn(['actionLabels', locale, 'remove']) : + 'Remove item', 'add-row': 'Add row', 'remove-row': 'Remove row', 'remove-rows': 'Remove rows', diff --git a/packages/docs/public/index.html b/packages/docs/public/index.html index 21a73f93..a657232c 100644 --- a/packages/docs/public/index.html +++ b/packages/docs/public/index.html @@ -20,6 +20,12 @@ Learn how to configure a non-root public URL by running `npm run build`. --> UI-Schema Documentation + diff --git a/packages/docs/src/component/Markdown.js b/packages/docs/src/component/Markdown.js index 49df6669..2a3b9ea2 100644 --- a/packages/docs/src/component/Markdown.js +++ b/packages/docs/src/component/Markdown.js @@ -28,13 +28,18 @@ renderers.p = p => inline ? : ; const MarkdownH = ({level, ...p}) => renderers.h1 = renderers.h2 = renderers.h3 = renderers.h4 = renderers.h5 = renderers.h6 = MarkdownH -renderers.li = p => {p.children}; +renderers.li = p => + {p.children} +; renderers.a = LinkInternalLocale; const renderersContent = baseRenderers(false); renderersContent.code = ({inline, ...p}) => inline ? : ; renderersContent.h1 = renderersContent.h2 = renderersContent.h3 = renderersContent.h4 = renderersContent.h5 = renderersContent.h6 = LinkableHeadline; renderersContent.a = LinkInternalLocale; +renderersContent.li = p => + {p.children} +; const rehypePlugins = [rehypeRaw] const remarkPlugins = [remarkGfm] diff --git a/packages/docs/src/content/docs.js b/packages/docs/src/content/docs.js index 6fe0ba29..6c1e29e2 100644 --- a/packages/docs/src/content/docs.js +++ b/packages/docs/src/content/docs.js @@ -136,6 +136,7 @@ export const routesFurtherDesignSystem = [ }, routes: [ createDoc('ds-material/Table', 'Table', ''), + createDoc('ds-material/GenericList', 'GenericList', ''), ], }, ], diff --git a/packages/docs/src/content/docs/ds-material/GenericList.md b/packages/docs/src/content/docs/ds-material/GenericList.md new file mode 100644 index 00000000..c10ad616 --- /dev/null +++ b/packages/docs/src/content/docs/ds-material/GenericList.md @@ -0,0 +1,129 @@ +# DS Material GenericList + +Base components for the `GenericList` widget, to easily configure and re-wire the widget parts, see also the [widget docs here](/docs/widgets/GenericList). + +```typescript jsx +import React from 'react' +import { List } from 'immutable' +import { memo } from '@ui-schema/ui-schema/Utils/memo' +import { WidgetProps } from '@ui-schema/ui-schema/Widget' +import { useUIStore, WithOnChange } from '@ui-schema/ui-schema/UIStore' +import { + GenericListContent, GenericListFooter, + GenericListItem, + GenericListItemMore, GenericListItemPos, +} from '@ui-schema/ds-material/BaseComponents/GenericList' +import { MuiWidgetBinding } from '@ui-schema/ds-material/widgetsBinding' + +// it is important to use `memo` from `@ui-schema/ui-schema` for the content component, +// as the generic list will re-render on each change of anything in the store, +// with passing down `listSize` and not other data, the `GenericListContent` will only re-render when the `listSize` changes +export const GenericListContentMemo = memo(GenericListContent) + +export const GenericList = (props: WidgetProps & WithOnChange): React.ReactElement => { + const {store} = useUIStore() + // info: `store?.extractValues` is new since `0.3.0-alpha.11` and can be used instead of the `extractValue` HOC + const {value} = store?.extractValues>(props.storeKeys) || {} + // extracting and calculating the list size here, not passing down the actual list for performance reasons + // https://github.com/ui-schema/ui-schema/issues/133 + return +} +``` + +Easily define own schema keywords, for options which are only supported by `props`: + +```typescript jsx +export const GenericList = (props: WidgetProps & WithOnChange): React.ReactElement => { + const {store} = useUIStore() + const {value} = store?.extractValues>(props.storeKeys) || {} + // extracting and calculating the list size here, not passing down the actual list for performance reasons + // https://github.com/ui-schema/ui-schema/issues/133 + return +} +``` + +Use the [GenericListContent.tsx](https://github.com/ui-schema/ui-schema/tree/master/packages/ds-material/src/BaseComponents/GenericList/GenericListContent.tsx) as a starting point if you need to further adjust the widget parts. + +## Translation / Labels + +- `labels.add-item` used by `GenericListFooter` for the add-button, supports named-labels key `add` +- `labels.remove-item` used by `GenericListItemMore` for the delete-button, supports named-labels key `remove` +- `labels.move-to-position` used by `GenericListItemPos` to render the up/down buttons, adds the context `nextIndex` to render to-which-position it will be moved +- `labels.entry` used by `GenericListItemPos` as `sr-only` for the nth-label + +**Notices:** + +- `sr-only` = `screen-reader-only` text `span` for a11y +- supports named-label: uses the `context` to pass down `actionLabels` for a not-generic-labelling, the given key is the intended action-key for the label + +For `actionLabels` handling, see [dictionary/en/labels](https://github.com/ui-schema/ui-schema/tree/master/packages/dictionary/en/labels.js) as an example. + +Example schema structure that activates `actionLabels`, with support for multiple languages and different buttons: + +```json +{ + "type": "object", + "widget": "GenericList", + "listActionLabels": { + "en": { + "add": "New event", + "remove": "Remove event" + }, + "de": { + "add": "Neue Veranstaltung", + "remove": "Lösche Veranstaltung" + } + } +} +``` + +## Components + +### GenericListContent + +The list renderer component, uses the components passed down per `props` to build the needed list out of the `listSize` - it does not receive any data from the list. + +`GenericListContentProps`: + +- `btnAddShowLabel`: `boolean`, for the add-item button, when `true` displays a normal button and not only an icon-button +- `btnAddStyle`: `React.CSSProperties`, for the add-item button, custom react styles +- `ComponentItemPos`: `React.ComponentType`, will be rendered in `ComponentItem`, contains the position and up/down buttons +- `ComponentItemMore`: `React.ComponentType`, will be rendered in `ComponentItem`, contains the delete button +- `ComponentItem`: `React.ComponentType`, is rendered per item in the list, responsible to further render nested schema, also uses component props +- `ComponentFooter`: `React.ComponentType`, will be rendered in `GenericListContent`, contains the add-button +- `listSize`: `number`, the size of the list +- `schemaKeys`: `StoreKeys`, experimental [#104](https://github.com/ui-schema/ui-schema/issues/104) +- `listSpacing`: `GridSpacing`, used as the spacing for the item-list + +### GenericListItem + +For props check typings. + +### GenericListItemPos + +For props check typings. + +### GenericListItemMore + +For props check typings. + +### GenericListFooter + +For props check typings. diff --git a/packages/docs/src/content/docs/ds-material/Table.md b/packages/docs/src/content/docs/ds-material/Table.md index f7739d12..71161d02 100644 --- a/packages/docs/src/content/docs/ds-material/Table.md +++ b/packages/docs/src/content/docs/ds-material/Table.md @@ -1,4 +1,4 @@ -# DS Material +# DS Material Table Base components for the `Table` widget, to build custom table widgets, check the [widget docs here](/docs/widgets/Table). diff --git a/packages/docs/src/content/docs/widgets/GenericList.md b/packages/docs/src/content/docs/widgets/GenericList.md index 9483bf5f..a17bd5ba 100644 --- a/packages/docs/src/content/docs/widgets/GenericList.md +++ b/packages/docs/src/content/docs/widgets/GenericList.md @@ -19,6 +19,8 @@ Widgets for complex structures in arrays. ### Material-UI +> see the [GenericList base components](https://ui-schema.bemit.codes/docs/ds-material/GenericList) for further customization and details about label translation + ```js import {GenericList} from "@ui-schema/ds-material/Widgets/GenericList"; @@ -32,8 +34,13 @@ const widgets = { **Supports extra keywords:** - `view` - - `btnSize` either `small` (default) or `medium` + - `btnSize` either `small` (default) or `medium`, used for the add-button + - `deleteBtnSize` either `small` (default) or `medium`, used for the delete-button + - `btnVariant` either `text | outlined | contained`, used for the add-button + - `btnVariant` either `text | outlined | contained`, used for the add-button + - `btnColor` either `inherit | primary | secondary | default`, used for the add-button - `hideTitle` when `true` doesn't show any title +- `listActionLabels` used for [named-label translation](https://ui-schema.bemit.codes/docs/ds-material/GenericList#translation--labels) - `notDeletable` when `true` doesn't allow deletion of items - `notAddable` when `true` doesn't allow adding items - `notSortable` when `true` doesn't allow sorting of items diff --git a/packages/docs/src/content/docs/widgets/Table.md b/packages/docs/src/content/docs/widgets/Table.md index 5531e690..5af9b2fc 100644 --- a/packages/docs/src/content/docs/widgets/Table.md +++ b/packages/docs/src/content/docs/widgets/Table.md @@ -4,13 +4,12 @@ Widget for data tables. [![Component Examples](https://img.shields.io/badge/Examples-green?labelColor=1d3d39&color=1a6754&logoColor=ffffff&style=flat-square&logo=plex)](#demo-ui-generator) [![supports Material-UI Binding](https://img.shields.io/badge/Material-green?labelColor=1a237e&color=0d47a1&logoColor=ffffff&style=flat-square&logo=material-ui)](#material-ui) -- type: `array(array | object)`, +- type: `array(array | object)` - widget keywords: - `Table` **(usable, beta)** - `TableAdvanced` **(in-dev)** - view - - does not support grid keywords in direct nested schemas, disables grid for own stack, but supports for nested `object` - + - does not support grid keywords in direct nested schemas, disables grid for own stack, but supported inside nested `object` - [Array Type Properties](/docs/schema#type-array) - [Object Type Properties](/docs/schema#type-object) @@ -26,6 +25,8 @@ Widget for data tables. Special `Table` component for complex, always validated, lists. Using custom widgets without labels. Hidden rows from pagination are still validated, using `isVirtual` prop. +> see the [table base components](https://ui-schema.bemit.codes/docs/ds-material/Table) for further customization + **Supports extra keywords:** - `view` @@ -85,8 +86,6 @@ customWidgets.custom = { Currently included cell components are based on the `TextField` components and support the same features - except label/title related options. The title related schema keywords are used by `Table` for the `TableHeader` cell contents. -> todo: more code sharing through utils - - `TextRendererCell` supports multi-line text - **extra keywords:** - `view` diff --git a/packages/ds-material/src/BaseComponents/GenericList/GenericListContent.tsx b/packages/ds-material/src/BaseComponents/GenericList/GenericListContent.tsx new file mode 100644 index 00000000..e8eea26a --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/GenericListContent.tsx @@ -0,0 +1,104 @@ +import React from 'react' +import FormControl from '@material-ui/core/FormControl' +import Grid from '@material-ui/core/Grid' +import FormLabel from '@material-ui/core/FormLabel' +import { TransTitle, StoreKeys, WidgetProps, WithOnChange } from '@ui-schema/ui-schema' +import { ListButtonOverwrites } from '@ui-schema/ds-material/Component/ListButton' +import { MuiWidgetBinding } from '@ui-schema/ds-material/widgetsBinding' +import { GenericListFooterProps, GenericListItemProps, GenericListItemSharedProps } from '@ui-schema/ds-material/BaseComponents' +import Box from '@material-ui/core/Box' +import { GridSpacing } from '@material-ui/core/Grid/Grid' + +export interface GenericListContentProps extends ListButtonOverwrites { + btnAddShowLabel?: boolean + btnAddStyle?: React.CSSProperties + ComponentItemPos?: React.ComponentType + ComponentItemMore?: React.ComponentType + ComponentItem: React.ComponentType + ComponentFooter?: React.ComponentType + listSize: number + schemaKeys?: StoreKeys + listSpacing?: GridSpacing +} + +export const GenericListContent =

>( + { + storeKeys, schemaKeys, ownKey, schema, listSize, onChange, + showValidity, valid, errors, required, level, widgets, + ComponentItemMore, ComponentItemPos, + ComponentItem, ComponentFooter, + btnAddShowLabel, btnAddStyle, + btnSize: btnSizeProp, + btnVariant: btnVariantProp, + btnColor: btnColorProp, + listSpacing = 3, + }: P & WithOnChange & GenericListContentProps, +): React.ReactElement => { + const btnSize = (schema.getIn(['view', 'btnSize']) || btnSizeProp || 'small') as ListButtonOverwrites['btnSize'] + const deleteBtnSize = (schema.getIn(['view', 'deleteBtnSize']) || btnSizeProp || 'small') as ListButtonOverwrites['btnSize'] + const btnVariant = (schema.getIn(['view', 'btnVariant']) || btnVariantProp || undefined) as ListButtonOverwrites['btnVariant'] + const btnColor = (schema.getIn(['view', 'btnColor']) || btnColorProp || undefined) as ListButtonOverwrites['btnColor'] + const notSortable = schema.get('notSortable') as boolean | undefined + const notAddable = schema.get('notAddable') as boolean | undefined + const notDeletable = schema.get('notDeletable') as boolean | undefined + const InfoRenderer = widgets?.InfoRenderer + + const info = InfoRenderer && schema?.get('info') ? + : null + + return + {!schema.getIn(['view', 'hideTitle']) ? + + + + + + + + {info} + : null} + + {schema.getIn(['view', 'hideTitle']) ? + {info} : null} + + + {Array(listSize).fill(null).map((_val, i) => + , + )} + + + {ComponentFooter ? + : null} + +} diff --git a/packages/ds-material/src/BaseComponents/GenericList/GenericListFooter.tsx b/packages/ds-material/src/BaseComponents/GenericList/GenericListFooter.tsx new file mode 100644 index 00000000..83aa8332 --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/GenericListFooter.tsx @@ -0,0 +1,69 @@ +import { ListButton, ListButtonOverwrites } from '@ui-schema/ds-material/Component/ListButton' +import Add from '@material-ui/icons/Add' +import { onChangeHandler, StoreKeys, StoreSchemaType, Trans, WidgetProps } from '@ui-schema/ui-schema' +import { Map } from 'immutable' +import { ValidityHelperText } from '@ui-schema/ds-material/Component/LocaleHelperText' +import React from 'react' +import Box from '@material-ui/core/Box' + +export interface GenericListFooterProps extends ListButtonOverwrites { + schema: StoreSchemaType + notAddable?: boolean + notSortable?: boolean + notDeletable?: boolean + onChange: onChangeHandler + storeKeys: StoreKeys + required?: boolean + btnAddShowLabel?: boolean + btnAddStyle?: React.CSSProperties + errors: WidgetProps['errors'] + showValidity: WidgetProps['showValidity'] +} + +export const GenericListFooter: React.ComponentType = ( + { + schema, required, + onChange, storeKeys, + notAddable, + btnColor, btnVariant, btnSize, + btnAddShowLabel, btnAddStyle, + errors, showValidity, + } +) => { + return + {!schema.get('readOnly') && !notAddable ? + { + onChange({ + storeKeys, + scopes: ['value', 'internal'], + type: 'list-item-add', + schema, + required, + }) + }} + btnSize={btnSize} + btnVariant={btnVariant} + btnColor={btnColor} + showLabel={btnAddShowLabel} + style={btnAddStyle} + Icon={Add} + title={ + + } + /> : null} + + + +} diff --git a/packages/ds-material/src/BaseComponents/GenericList/GenericListItem.tsx b/packages/ds-material/src/BaseComponents/GenericList/GenericListItem.tsx new file mode 100644 index 00000000..fc54910a --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/GenericListItem.tsx @@ -0,0 +1,94 @@ +import React from 'react' +import Grid from '@material-ui/core/Grid' +import Divider from '@material-ui/core/Divider' +import { memo, PluginStack, schemaTypeToDistinct, StoreSchemaType, onChangeHandler, StoreKeys, SchemaTypesType } from '@ui-schema/ui-schema' +import { List } from 'immutable' +import { ListButtonOverwrites } from '@ui-schema/ds-material/Component/ListButton' +import { GridSpacing } from '@material-ui/core/Grid/Grid' + +export interface GenericListItemSharedProps { + index: number + listSize: number + listRequired: boolean + schema: StoreSchemaType + onChange: onChangeHandler + storeKeys: StoreKeys + schemaKeys: StoreKeys | undefined + level: number + notSortable: boolean | undefined + notDeletable: boolean | undefined + showValidity: boolean | undefined + btnSize?: ListButtonOverwrites['btnSize'] +} + +export interface GenericListItemComponents { + ComponentPos?: React.ComponentType + ComponentMore?: React.ComponentType +} + +export type GenericListItemProps = GenericListItemSharedProps & GenericListItemComponents & { spacing?: GridSpacing } + +export const GenericListItemBase = ( + { + ComponentPos, + ComponentMore, + spacing = 2, + btnSize, ...props + }: GenericListItemProps, +): React.ReactElement => { + const { + index, listSize, schema, + storeKeys, schemaKeys, level, + showValidity, + } = props + const ownKeys = storeKeys.push(index) + const itemsSchema = schema.get('items') as StoreSchemaType + + return [ + + + {ComponentPos ? + + + : null} + + {itemsSchema && List.isList(itemsSchema) ? + // tuples in root level not possible + // was wrong implementation <= 0.2.2 + null : + schemaTypeToDistinct(itemsSchema.get('type') as SchemaTypesType) === 'array' && + itemsSchema.get('items') ? + + + {(itemsSchema.get('items') as StoreSchemaType)?.map((item, j) => + + key={j} + showValidity={showValidity} + schemaKeys={schemaKeys?.push('items').push('items').push(j)} + storeKeys={ownKeys.push(j)} + schema={item as StoreSchemaType} + parentSchema={schema} + level={level + 1} + />).valueSeq()} + + : + + showValidity={showValidity} + schema={itemsSchema} parentSchema={schema} + storeKeys={ownKeys} level={level + 1} + schemaKeys={schemaKeys?.push('items')} + />} + + {ComponentMore ? + + + : null} + + , + index < listSize - 1 ? : null, + ] as unknown as React.ReactElement +} +export const GenericListItem = memo(GenericListItemBase) diff --git a/packages/ds-material/src/BaseComponents/GenericList/GenericListItemMore.tsx b/packages/ds-material/src/BaseComponents/GenericList/GenericListItemMore.tsx new file mode 100644 index 00000000..481d619f --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/GenericListItemMore.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import IconButton from '@material-ui/core/IconButton' +import { Trans } from '@ui-schema/ui-schema' +import { AccessTooltipIcon } from '@ui-schema/ds-material/Component/Tooltip' +import { GenericListItemSharedProps } from '@ui-schema/ds-material' +import Delete from '@material-ui/icons/Delete' +import { Map } from 'immutable' + +export const GenericListItemMore = ( + { + index, listRequired, schema, + onChange, storeKeys, notDeletable, btnSize = 'small', + }: GenericListItemSharedProps, +): React.ReactElement => { + const readOnly = schema.get('readOnly') + return + {!readOnly && !notDeletable ? + + onChange({ + storeKeys, + scopes: ['value', 'internal'], + type: 'list-item-delete', + index: index, + schema, + required: listRequired, + }) + } + size={btnSize} + style={{margin: '0 0 auto 0'}} + > + + } + > + + + : null} + +} diff --git a/packages/ds-material/src/BaseComponents/GenericList/GenericListItemPos.tsx b/packages/ds-material/src/BaseComponents/GenericList/GenericListItemPos.tsx new file mode 100644 index 00000000..45f0c0a7 --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/GenericListItemPos.tsx @@ -0,0 +1,70 @@ +import React from 'react' +import IconButton from '@material-ui/core/IconButton' +import Typography from '@material-ui/core/Typography' +import KeyboardArrowUp from '@material-ui/icons/KeyboardArrowUp' +import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown' +import { Trans } from '@ui-schema/ui-schema' +import { Map } from 'immutable' +import { AccessTooltipIcon } from '@ui-schema/ds-material/Component/Tooltip/Tooltip' +import { GenericListItemSharedProps } from '@ui-schema/ds-material' + +export const GenericListItemPos = ( + { + index, listSize, listRequired, schema, + onChange, storeKeys, notSortable, btnSize = 'small', + }: GenericListItemSharedProps, +): React.ReactElement => { + const readOnly = schema.get('readOnly') + return + {!readOnly && !notSortable && index > 0 ? + + onChange({ + storeKeys, + scopes: ['value', 'internal'], + type: 'list-item-move', + fromIndex: index, + toIndex: index - 1, + schema, + required: listRequired, + }) + } + > + }> + + + : null} + + + {index + 1}. + + + + + + {!readOnly && !notSortable && index < listSize - 1 ? + + onChange({ + storeKeys, + scopes: ['value', 'internal'], + type: 'list-item-move', + fromIndex: index, + toIndex: index + 1, + schema, + required: listRequired, + }) + } + > + }> + + + : null} + +} diff --git a/packages/ds-material/src/BaseComponents/GenericList/index.ts b/packages/ds-material/src/BaseComponents/GenericList/index.ts new file mode 100644 index 00000000..896a4b0a --- /dev/null +++ b/packages/ds-material/src/BaseComponents/GenericList/index.ts @@ -0,0 +1,5 @@ +export * from './GenericListContent' +export * from './GenericListItem' +export * from './GenericListItemPos' +export * from './GenericListItemMore' +export * from './GenericListFooter' diff --git a/packages/ds-material/src/BaseComponents/index.ts b/packages/ds-material/src/BaseComponents/index.ts index a86149cb..13d89b8d 100644 --- a/packages/ds-material/src/BaseComponents/index.ts +++ b/packages/ds-material/src/BaseComponents/index.ts @@ -1 +1,2 @@ export * from './Table' +export * from './GenericList' diff --git a/packages/ds-material/src/Component/ListButton/ListButton.tsx b/packages/ds-material/src/Component/ListButton/ListButton.tsx new file mode 100644 index 00000000..93cbbf5c --- /dev/null +++ b/packages/ds-material/src/Component/ListButton/ListButton.tsx @@ -0,0 +1,56 @@ +import { AccessTooltipIcon } from '@ui-schema/ds-material' +import Button, { ButtonProps } from '@material-ui/core/Button' +import IconButton, { IconButtonProps } from '@material-ui/core/IconButton' +import React from 'react' + +export interface ListButtonOverwrites { + btnSize?: IconButtonProps['size'] + // only for `Button`, not for `IconButton` + btnVariant?: ButtonProps['variant'] + btnColor?: ButtonProps['color'] +} + +export interface ListButtonProps extends ListButtonOverwrites { + onClick: () => void + Icon?: React.ComponentType<{ + fontSize?: 'inherit' | 'small' | 'medium' | 'large' + style?: React.CSSProperties + }> + title: React.ReactElement | string + style?: React.CSSProperties + showLabel?: boolean +} + +export const ListButton: React.ComponentType = ( + { + onClick, + Icon, title, + btnSize, btnVariant, btnColor, + style, showLabel, + } +) => { + return <> + {!showLabel && Icon ? + + + + + : + } + +} diff --git a/packages/ds-material/src/Component/ListButton/index.ts b/packages/ds-material/src/Component/ListButton/index.ts new file mode 100644 index 00000000..39970bd3 --- /dev/null +++ b/packages/ds-material/src/Component/ListButton/index.ts @@ -0,0 +1 @@ +export * from './ListButton' diff --git a/packages/ds-material/src/Component/Tooltip/Tooltip.d.ts b/packages/ds-material/src/Component/Tooltip/Tooltip.d.ts index 42cb0af3..5b90cfdb 100644 --- a/packages/ds-material/src/Component/Tooltip/Tooltip.d.ts +++ b/packages/ds-material/src/Component/Tooltip/Tooltip.d.ts @@ -1,4 +1,4 @@ import * as React from 'react' -// render the `icon` simply as `children`, must allow to accept refs -export function AccessTooltipIcon(props: React.PropsWithChildren<{ title: string }>): React.ReactElement +// render the `icon` simply as `children`, must allow accepting refs +export function AccessTooltipIcon(props: React.PropsWithChildren<{ title: string | React.ReactElement }>): React.ReactElement diff --git a/packages/ds-material/src/Component/Tooltip/Tooltip.js b/packages/ds-material/src/Component/Tooltip/Tooltip.js index 90fff414..6f4c2e45 100644 --- a/packages/ds-material/src/Component/Tooltip/Tooltip.js +++ b/packages/ds-material/src/Component/Tooltip/Tooltip.js @@ -1,8 +1,8 @@ -import React from "react"; -import Tooltip from "@material-ui/core/Tooltip"; -import Typography from "@material-ui/core/Typography"; +import React from 'react'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; -const AccessTooltipIcon = ({title, children}) => +export const AccessTooltipIcon = ({title, children}) => {children} @@ -10,5 +10,3 @@ const AccessTooltipIcon = ({title, children}) => {title} ; - -export {AccessTooltipIcon} diff --git a/packages/ds-material/src/Component/index.d.ts b/packages/ds-material/src/Component/index.d.ts index 88e943d5..6c21eaaa 100644 --- a/packages/ds-material/src/Component/index.d.ts +++ b/packages/ds-material/src/Component/index.d.ts @@ -1,3 +1,4 @@ -export * from './LocaleHelperText' export * from './InfoRenderer' +export * from './ListButton' +export * from './LocaleHelperText' export * from './Tooltip' diff --git a/packages/ds-material/src/Component/index.js b/packages/ds-material/src/Component/index.js index 6002e8a4..6c21eaaa 100644 --- a/packages/ds-material/src/Component/index.js +++ b/packages/ds-material/src/Component/index.js @@ -1,4 +1,4 @@ -export * from './LocaleHelperText' export * from './InfoRenderer' +export * from './ListButton' +export * from './LocaleHelperText' export * from './Tooltip' - diff --git a/packages/ds-material/src/Widgets/GenericList/GenericList.d.ts b/packages/ds-material/src/Widgets/GenericList/GenericList.d.ts deleted file mode 100644 index 2ab4538a..00000000 --- a/packages/ds-material/src/Widgets/GenericList/GenericList.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as React from 'react' -import { WidgetProps } from '@ui-schema/ui-schema/Widget' - -export function GenericList

(props: P): React.ReactElement

diff --git a/packages/ds-material/src/Widgets/GenericList/GenericList.js b/packages/ds-material/src/Widgets/GenericList/GenericList.js deleted file mode 100644 index 7417bce2..00000000 --- a/packages/ds-material/src/Widgets/GenericList/GenericList.js +++ /dev/null @@ -1,217 +0,0 @@ -import React from 'react'; -import FormControl from '@material-ui/core/FormControl'; -import Grid from '@material-ui/core/Grid'; -import FormLabel from '@material-ui/core/FormLabel'; -import IconButton from '@material-ui/core/IconButton'; -import Typography from '@material-ui/core/Typography'; -import Divider from '@material-ui/core/Divider'; -import Add from '@material-ui/icons/Add'; -import Delete from '@material-ui/icons/Delete'; -import KeyboardArrowUp from '@material-ui/icons/KeyboardArrowUp'; -import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown'; -import {TransTitle, extractValue, memo, PluginStack, Trans, schemaTypeToDistinct} from '@ui-schema/ui-schema'; -import {ValidityHelperText} from '@ui-schema/ds-material/Component/LocaleHelperText/LocaleHelperText'; -import {List, Map} from 'immutable'; -import {AccessTooltipIcon} from '@ui-schema/ds-material/Component/Tooltip/Tooltip'; - -let GenericListItem = ( - { - index, listSize, schema, listRequired, notSortable, notDeletable, showValidity, onChange, storeKeys, schemaKeys, level, btnSize, - }, -) => { - const ownKeys = storeKeys.push(index) - const itemsSchema = schema.get('items') - const readOnly = schema.get('readOnly') - return - - - - - {!readOnly && !notSortable && index > 0 ? - onChange({ - storeKeys, - scopes: ['value', 'internal'], - type: 'list-item-move', - fromIndex: index, - toIndex: index - 1, - schema, - required: listRequired, - }) - } - > - }> - - - : null} - - - {index + 1}. - - - - - - {!readOnly && !notSortable && index < listSize - 1 ? - onChange({ - storeKeys, - scopes: ['value', 'internal'], - type: 'list-item-move', - fromIndex: index, - toIndex: index + 1, - schema, - required: listRequired, - }) - } - > - }> - - - : null} - - - - {List.isList(itemsSchema) ? - // tuples in root level not possible - // was wrong implementation <= 0.2.2 - null : - schemaTypeToDistinct(itemsSchema.get('type')) === 'array' && - itemsSchema.get('items') ? - - - {itemsSchema.get('items')?.map((item, j) => ).valueSeq()} - - : - } - - {!readOnly && !notDeletable ? - - onChange({ - storeKeys, - scopes: ['value', 'internal'], - type: 'list-item-delete', - index: index, - schema, - required: listRequired, - }) - } - size={btnSize} - style={{margin: '0 0 auto 0'}} - > - }> - - - - : null} - - - {index < listSize - 1 ? : null} - -} -GenericListItem = memo(GenericListItem) - -let GenericListBase = ({ - storeKeys, schemaKeys, ownKey, schema, listSize, onChange, - showValidity, valid, errors, required, level, widgets, - }) => { - const btnSize = schema.getIn(['view', 'btnSize']) || 'small'; - const notSortable = schema.get('notSortable') - const notAddable = schema.get('notAddable') - const notDeletable = schema.get('notDeletable') - const InfoRenderer = widgets?.InfoRenderer - - return - - {!schema.getIn(['view', 'hideTitle']) ? - - : null} - - {InfoRenderer && schema?.get('info') ? - - - : undefined} - - {Array(listSize).fill(null).map((val, i) => - , - )} - - - {!schema.get('readOnly') && !notAddable ? - { - onChange({ - storeKeys, - scopes: ['value', 'internal'], - type: 'list-item-add', - schema, - required, - }) - }} - size={btnSize} - > - }> - - - : null} - - - - - -} -GenericListBase = memo(GenericListBase) - -export const GenericListExtractor = ( - { - value, - // remove `internalValue` from the widget, performance optimize - // eslint-disable-next-line no-unused-vars - internalValue, - ...props - }, -) => { - // extracting and calculating the list size here, not passing down the actual list for performance reasons - // https://github.com/ui-schema/ui-schema/issues/133 - return -} - -export const GenericList = extractValue(memo(GenericListExtractor)) diff --git a/packages/ds-material/src/Widgets/GenericList/GenericList.tsx b/packages/ds-material/src/Widgets/GenericList/GenericList.tsx new file mode 100644 index 00000000..6ea87d82 --- /dev/null +++ b/packages/ds-material/src/Widgets/GenericList/GenericList.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { List } from 'immutable' +import { memo } from '@ui-schema/ui-schema/Utils/memo' +import { WidgetProps } from '@ui-schema/ui-schema/Widget' +import { useUIStore, WithOnChange } from '@ui-schema/ui-schema/UIStore' +import { + GenericListContent, GenericListFooter, + GenericListItem, + GenericListItemMore, GenericListItemPos, +} from '@ui-schema/ds-material/BaseComponents/GenericList' +import { MuiWidgetBinding } from '@ui-schema/ds-material/widgetsBinding' + +export const GenericListContentMemo = memo(GenericListContent) + +export const GenericList = (props: WidgetProps & WithOnChange): React.ReactElement => { + const {store} = useUIStore() + const {value} = store?.extractValues>(props.storeKeys) || {} + // extracting and calculating the list size here, not passing down the actual list for performance reasons + // https://github.com/ui-schema/ui-schema/issues/133 + return +} + diff --git a/packages/ds-material/src/Widgets/GenericList/index.js b/packages/ds-material/src/Widgets/GenericList/index.js deleted file mode 100644 index 164be159..00000000 --- a/packages/ds-material/src/Widgets/GenericList/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './GenericList' diff --git a/packages/ds-material/src/Widgets/GenericList/index.d.ts b/packages/ds-material/src/Widgets/GenericList/index.ts similarity index 100% rename from packages/ds-material/src/Widgets/GenericList/index.d.ts rename to packages/ds-material/src/Widgets/GenericList/index.ts diff --git a/packages/ds-material/src/widgetsBinding.tsx b/packages/ds-material/src/widgetsBinding.tsx index cb55e01b..3e852098 100644 --- a/packages/ds-material/src/widgetsBinding.tsx +++ b/packages/ds-material/src/widgetsBinding.tsx @@ -14,7 +14,7 @@ import { pluginStack } from './pluginStack' import { WidgetRenderer } from '@ui-schema/ui-schema/WidgetRenderer' import { validators } from '@ui-schema/ui-schema/Validators/validators' import { CardRenderer, FormGroup, LabelBox } from '@ui-schema/ds-material/Widgets' -import { ErrorFallbackProps, WidgetProps, WidgetsBindingFactory, WidgetType, WithScalarValue } from '@ui-schema/ui-schema' +import { ErrorFallbackProps, UIStoreActions, WidgetProps, WidgetsBindingFactory, WidgetType, WithScalarValue } from '@ui-schema/ui-schema' import { InfoRendererProps } from '@ui-schema/ds-material/Component/InfoRenderer' import { List } from 'immutable' @@ -33,24 +33,8 @@ export interface MuiWidgetsBindingTypes & C & WithScalarValue> } -export interface MuiWidgetsBindingCustom { - Accordions: React.ComponentType & C> - Text: React.ComponentType & C & WithScalarValue> - StringIcon: React.ComponentType & C & WithScalarValue> - TextIcon: React.ComponentType & C & WithScalarValue> - NumberIcon: React.ComponentType & C & WithScalarValue> - NumberSlider: React.ComponentType & C> - SimpleList: React.ComponentType & C> - GenericList: React.ComponentType & C> - OptionsCheck: React.ComponentType & C> - OptionsRadio: React.ComponentType & C> - Select: React.ComponentType & C & WithScalarValue> - SelectMulti: React.ComponentType & C> - Card: React.ComponentType & C> - LabelBox: React.ComponentType & C> - FormGroup: React.ComponentType & C> - - [key: string]: WidgetType | WidgetType +export interface MuiWidgetsBindingCustom { + [key: string]: WidgetType | WidgetType } export interface MuiWidgetBindingExtra { diff --git a/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.d.ts b/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.d.ts index 98ddebd9..efee789c 100644 --- a/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.d.ts +++ b/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.d.ts @@ -12,6 +12,8 @@ export type Translator = ( ) => translation export const makeTranslator: ( - dictionary: Map | string | number | Function | React.ComponentType }>, + dictionary: Map | string | number | Function | React.ComponentType + }>, locale?: string ) => Translator diff --git a/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.js b/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.js index 5f169a44..ec6d94ea 100644 --- a/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.js +++ b/packages/ui-schema/src/Translate/makeTranslator/makeTranslator.js @@ -21,7 +21,7 @@ export const makeTranslator = (dictionary, locale = '') => let trans = dictionary.getIn(text.split('.')); if(typeof trans === 'function') { - return trans(context); + return trans(context, locale); } return trans; diff --git a/packages/ui-schema/src/Widget.ts b/packages/ui-schema/src/Widget.ts index f17b6fe3..677b4e0c 100644 --- a/packages/ui-schema/src/Widget.ts +++ b/packages/ui-schema/src/Widget.ts @@ -11,7 +11,7 @@ export type WidgetOverrideType & C & WithOnChange> | React.ComponentType

& C & WithScalarValue> -export type WidgetType = WidgetOverrideType +export type WidgetType = WidgetOverrideType /** * Base widget props which are expected to exist no matter which data "type" the widget is for