From 25ae3a64fd337d50d73f72cb8c1b59119ba85eb1 Mon Sep 17 00:00:00 2001 From: Denys Oblohin Date: Sun, 30 Jul 2023 14:55:05 +0300 Subject: [PATCH] Add `renderIcon` to customize button icons and drag icon (#962) * Add renderIcon * fluent * bootstrap * antd * . * mui * iconProps * chlog * lint fix * type fix --- CHANGELOG.md | 2 + CONFIG.adoc | 2 +- packages/antd/modules/config/index.jsx | 1 + packages/antd/modules/widgets/core/Button.jsx | 69 +++++++++--------- packages/antd/modules/widgets/core/Icon.jsx | 24 +++++++ packages/antd/modules/widgets/index.jsx | 2 + packages/bootstrap/modules/config/index.jsx | 1 + .../modules/widgets/core/BootstrapButton.jsx | 55 +++++++------- .../modules/widgets/core/BootstrapIcon.jsx | 28 ++++++++ packages/bootstrap/modules/widgets/index.jsx | 2 + packages/bootstrap/styles/fixes.scss | 6 ++ packages/core/modules/config/default.js | 4 +- .../core/modules/utils/configSerialize.js | 1 + packages/fluent/modules/config/index.jsx | 1 + .../modules/widgets/core/FluentUIButton.jsx | 71 +++++++++++-------- .../modules/widgets/core/FluentUIIcon.jsx | 41 +++++++++++ packages/fluent/modules/widgets/index.jsx | 2 + packages/material/modules/config/index.jsx | 1 + .../modules/widgets/core/MaterialButton.jsx | 69 +++++++++++------- .../modules/widgets/core/MaterialIcon.jsx | 46 ++++++++++++ packages/material/modules/widgets/index.jsx | 2 + packages/mui/modules/config/index.jsx | 1 + .../mui/modules/widgets/core/MuiButton.jsx | 49 ++++++------- packages/mui/modules/widgets/core/MuiIcon.jsx | 46 ++++++++++++ packages/mui/modules/widgets/index.jsx | 2 + packages/tests/specs/Compress.test.ts | 1 + packages/ui/modules/components/item/Group.jsx | 20 +++--- .../modules/components/item/GroupActions.jsx | 9 +-- packages/ui/modules/components/item/Rule.jsx | 25 ++++--- .../components/item/RuleGroupActions.jsx | 7 +- .../components/item/RuleGroupExtActions.jsx | 7 +- .../components/item/SwitchGroupActions.jsx | 7 +- .../widgets/vanilla/core/VanillaButton.jsx | 17 ++--- .../widgets/vanilla/core/VanillaIcon.jsx | 13 ++++ .../components/widgets/vanilla/index.jsx | 1 + packages/ui/modules/config/index.jsx | 1 + packages/ui/modules/index.d.ts | 15 +++- packages/ui/modules/utils/index.js | 3 +- 38 files changed, 470 insertions(+), 184 deletions(-) create mode 100644 packages/antd/modules/widgets/core/Icon.jsx create mode 100644 packages/bootstrap/modules/widgets/core/BootstrapIcon.jsx create mode 100644 packages/fluent/modules/widgets/core/FluentUIIcon.jsx create mode 100644 packages/material/modules/widgets/core/MaterialIcon.jsx create mode 100644 packages/mui/modules/widgets/core/MuiIcon.jsx create mode 100644 packages/ui/modules/components/widgets/vanilla/core/VanillaIcon.jsx diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c31bfc0..2a1fca1f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Changelog +- 6.4.2 + - Allow override icons with `renderIcon` (issues #319, #872) (PR #xxx) - 6.4.1 - Fixed import of rule_group in rule_group from SpEL (PR #959) - Updated type `ItemBuilderProps` (PR #959) diff --git a/CONFIG.adoc b/CONFIG.adoc index e006b95d4..ac6599225 100644 --- a/CONFIG.adoc +++ b/CONFIG.adoc @@ -367,7 +367,7 @@ Render settings: Available widgets for AntDesign: `FieldSelect`, `FieldDropdown` |renderFunc |`(props) => ` |Render functions list + Available widgets for AntDesign: `FieldSelect`, `FieldDropdown` -|renderConjs, renderButton, renderButtonGroup, renderSwitch, renderProvider, renderValueSources, renderConfirm, useConfirm, renderRuleError | |Other internal render functions you can override if using another UI framework ({renderSwitch}[example]) +|renderConjs, renderButton, renderIcon, renderButtonGroup, renderSwitch, renderProvider, renderValueSources, renderConfirm, useConfirm, renderRuleError | |Other internal render functions you can override if using another UI framework ({renderSwitch}[example]) |renderItem | |Render Item + Able to Customize Render behavior for rule/group items. |showLabels |false |Show labels above all fields? diff --git a/packages/antd/modules/config/index.jsx b/packages/antd/modules/config/index.jsx index 1c829b3d6..4bb556fb6 100644 --- a/packages/antd/modules/config/index.jsx +++ b/packages/antd/modules/config/index.jsx @@ -21,6 +21,7 @@ const settings = { renderConjs: (props, {RCE, W: {Conjs}}) => RCE(Conjs, props), renderSwitch: (props, {RCE, W: {Switch}}) => RCE(Switch, props), renderButton: (props, {RCE, W: {Button}}) => RCE(Button, props), + renderIcon: (props, {RCE, W: {Icon}}) => RCE(Icon, props), renderButtonGroup: (props, {RCE, W: {ButtonGroup}}) => RCE(ButtonGroup, props), renderValueSources: (props, {RCE, W: {ValueSources}}) => RCE(ValueSources, props), renderFieldSources: (props, {RCE, W: {ValueSources}}) => RCE(ValueSources, props), diff --git a/packages/antd/modules/widgets/core/Button.jsx b/packages/antd/modules/widgets/core/Button.jsx index 411624554..7ab6cabdb 100644 --- a/packages/antd/modules/widgets/core/Button.jsx +++ b/packages/antd/modules/widgets/core/Button.jsx @@ -1,53 +1,50 @@ import React from "react"; import { Button } from "antd"; -import { PlusOutlined, PlusCircleOutlined, DeleteFilled } from "@ant-design/icons"; -export default ({type, onClick, label, readonly, config: {settings}}) => { - const hideLabelsFor = { - "addRuleGroup": true - }; - const btnLabel = hideLabelsFor[type] ? "" : label; - const hasLabel = !!btnLabel; - - const typeToIcon = { - "addRule": , - "addGroup": , - "delRule": , //? - "delGroup": , - "delRuleGroup": , - "addRuleGroup": , - }; +const hideLabelsFor = { + "addRuleGroup": true, + "delGroup": true, + "delRuleGroup": true, + "delRule": true, +}; - const typeToClass = { - "addRule": "action action--ADD-RULE", - "addGroup": "action action--ADD-GROUP", - "delRule": "action action--DELETE", //? - "delGroup": "action action--DELETE", - "delRuleGroup": "action action--DELETE", - "addRuleGroup": , - }; +const typeToClass = { + "addRule": "action action--ADD-RULE", + "addGroup": "action action--ADD-GROUP", + "delRule": "action action--DELETE", + "delGroup": "action action--DELETE", + "delRuleGroup": "action action--DELETE", + "addRuleGroup": "action action--ADD-RULE", +}; - const typeToType = { - "delRule": "text", - // "delGroup": "default", - // "delRuleGroup": "default", - }; +const typeToType = { + "delRule": "text", + // "delGroup": "default", + // "delRuleGroup": "default", +}; - const dangerFor = { - "delRule": true, - "delGroup": true, - "delRuleGroup": true, - }; +const dangerFor = { + "delRule": true, + "delGroup": true, + "delRuleGroup": true, +}; +export default (props) => { + const {type, onClick, label, readonly, config: {settings}, renderIcon} = props; const {renderSize} = settings; - + const iconProps = { + type, + readonly, + }; + const icon = renderIcon?.(iconProps); + const btnLabel = hideLabelsFor[type] ? "" : label; return ( - ); + if (!onClick) { + return Icon; + } else { + return ( + + ); + } }; diff --git a/packages/bootstrap/modules/widgets/core/BootstrapIcon.jsx b/packages/bootstrap/modules/widgets/core/BootstrapIcon.jsx new file mode 100644 index 000000000..ab813a95e --- /dev/null +++ b/packages/bootstrap/modules/widgets/core/BootstrapIcon.jsx @@ -0,0 +1,28 @@ +import React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPlus, faTrashAlt, faUpDown } from "@fortawesome/free-solid-svg-icons"; +import { Utils } from "@react-awesome-query-builder/ui"; +const { DragIcon } = Utils; + +const typeToIcon = { + delGroup: faTrashAlt, + delRuleGroup: faTrashAlt, + delRule: faTrashAlt, + addRuleGroup: faPlus, + addRuleGroupExt: faPlus, + addRule: faPlus, + addGroup: faPlus, + drag: faUpDown, +}; + +export default ({ type }) => { + let icon = typeToIcon[type] + && ; + if (!icon && type === "drag") { + icon = ; + } + + return icon; +}; diff --git a/packages/bootstrap/modules/widgets/index.jsx b/packages/bootstrap/modules/widgets/index.jsx index bfa2fd463..4287dc0f9 100644 --- a/packages/bootstrap/modules/widgets/index.jsx +++ b/packages/bootstrap/modules/widgets/index.jsx @@ -14,6 +14,7 @@ import BootstrapMultiSelectWidget from "./value/BootstrapMultiSelect"; import BootstrapFieldSelect from "./core/BootstrapFieldSelect"; // core components +import BootstrapIcon from "./core/BootstrapIcon"; import BootstrapButton from "./core/BootstrapButton"; import BootstrapButtonGroup from "./core/BootstrapButtonGroup"; import BootstrapConjs from "./core/BootstrapConjs"; @@ -37,6 +38,7 @@ export default { BootstrapFieldSelect, + BootstrapIcon, BootstrapButton, BootstrapButtonGroup, BootstrapConjs, diff --git a/packages/bootstrap/styles/fixes.scss b/packages/bootstrap/styles/fixes.scss index c27907cd1..2222ce9a6 100644 --- a/packages/bootstrap/styles/fixes.scss +++ b/packages/bootstrap/styles/fixes.scss @@ -1,3 +1,9 @@ .svg-inline--fa { pointer-events: none; } +.btn > .svg-inline--fa { + margin-right: 4px; +} +.rule--drag-handler .svg-inline--fa { + margin-left: 6px; +} diff --git a/packages/core/modules/config/default.js b/packages/core/modules/config/default.js index 169323fe6..5c023ea1b 100644 --- a/packages/core/modules/config/default.js +++ b/packages/core/modules/config/default.js @@ -46,14 +46,14 @@ export const settings = { operatorPlaceholder: "Select operator", lockLabel: "Lock", lockedLabel: "Locked", - deleteLabel: null, + deleteLabel: "Delete", addGroupLabel: "Add group", addCaseLabel: "Add condition", addDefaultCaseLabel: "Add default condition", defaultCaseLabel: "Default:", addRuleLabel: "Add rule", addSubRuleLabel: "Add sub rule", - delGroupLabel: "", + delGroupLabel: "Delete", notLabel: "Not", fieldSourcesPopupTitle: "Select source", valueSourcesPopupTitle: "Select value source", diff --git a/packages/core/modules/utils/configSerialize.js b/packages/core/modules/utils/configSerialize.js index 8340a3473..ce3c199a4 100644 --- a/packages/core/modules/utils/configSerialize.js +++ b/packages/core/modules/utils/configSerialize.js @@ -134,6 +134,7 @@ const compileMetaSettings = { renderFunc: { type: "rf" }, renderConjs: { type: "rf" }, renderButton: { type: "rf" }, + renderIcon: { type: "rf" }, renderButtonGroup: { type: "rf" }, renderValueSources: { type: "rf" }, renderFieldSources: { type: "rf" }, diff --git a/packages/fluent/modules/config/index.jsx b/packages/fluent/modules/config/index.jsx index 7bf4c4bb9..8e3137f81 100644 --- a/packages/fluent/modules/config/index.jsx +++ b/packages/fluent/modules/config/index.jsx @@ -11,6 +11,7 @@ const settings = { renderFunc: (props, {RCE, W: {FluentUIFieldSelect}}) => RCE(FluentUIFieldSelect, props), renderConjs: (props, {RCE, W: {FluentUIConjs}}) => RCE(FluentUIConjs, props), renderButton: (props, {RCE, W: {FluentUIButton}}) => RCE(FluentUIButton, props), + renderIcon: (props, {RCE, W: {FluentUIIcon}}) => RCE(FluentUIIcon, props), renderButtonGroup: (props, {RCE, W: {FluentUIButtonGroup}}) => RCE(FluentUIButtonGroup, props), renderValueSources: (props, {RCE, W: {FluentUIValueSources}}) => RCE(FluentUIValueSources, props), renderFieldSources: (props, {RCE, W: {FluentUIValueSources}}) => RCE(FluentUIValueSources, props), diff --git a/packages/fluent/modules/widgets/core/FluentUIButton.jsx b/packages/fluent/modules/widgets/core/FluentUIButton.jsx index b0f0db4f7..58b809a3e 100644 --- a/packages/fluent/modules/widgets/core/FluentUIButton.jsx +++ b/packages/fluent/modules/widgets/core/FluentUIButton.jsx @@ -1,53 +1,46 @@ import React from "react"; -import { IconButton, ActionButton, CommandBarButton } from "@fluentui/react"; +import { IconButton, ActionButton, CommandBarButton, DefaultButton } from "@fluentui/react"; -const FluentUIButton = (props) => { - var type = props.type, - label = props.label, - onClick = props.onClick, - readonly = props.readonly; +const hideLabelsFor = { + "addRuleGroup": true, + "delRuleGroup": true, + "delRule": true, + // "addRuleGroupExt": true, + // "delGroup": true, +}; +const useAction = { + "addRuleGroup": true, +}; - const hideLabelsFor = { - "addRuleGroup": true, - }; - var typeToIcon = { - addRuleGroup: "CirclePlus", - }; - var typeToCommandIcon = { - addRuleGroupExt: "Add", - addRule: "Add", - addGroup: "CirclePlus", - delGroup: "Delete", - delRuleGroup: "Delete", - delRule: "Delete", - }; +const FluentUIButton = (props) => { + const { type, label, onClick, readonly, renderIcon } = props; + let renderBtn; if (!label || hideLabelsFor[type]) { - return ( + renderBtn = (bprops) => ( ); - } else if (typeToIcon[type]) { - return ( + } else if (useAction[type]) { + renderBtn = (bprops) => ( ); - } else if (typeToCommandIcon[type]) { - return ( + } else { + renderBtn = (bprops) => ( { backgroundColor: "transparent" } }} + {...bprops} /> ); } + + const renderDefaultButton = (bprops) => ( + + ); + + const iconProps = { + type, + readonly, + renderBtn, + renderDefaultButton, + }; + const buttonIcon = renderIcon?.(iconProps); + return buttonIcon; + }; export default FluentUIButton; diff --git a/packages/fluent/modules/widgets/core/FluentUIIcon.jsx b/packages/fluent/modules/widgets/core/FluentUIIcon.jsx new file mode 100644 index 000000000..066ee5504 --- /dev/null +++ b/packages/fluent/modules/widgets/core/FluentUIIcon.jsx @@ -0,0 +1,41 @@ +import React from "react"; +import { Icon } from "@fluentui/react"; +import { Utils } from "@react-awesome-query-builder/ui"; +const { DragIcon } = Utils; + +const typeToIcon = { + addRuleGroup: "CirclePlus", + addRuleGroupExt: "Add", + addRule: "Add", + addGroup: "CirclePlus", + delGroup: "Delete", + delRuleGroup: "Delete", + delRule: "Delete", + drag: "GripperBarHorizontal", +}; + +const FluentUIIcon = ({ type, readonly, renderBtn, renderDefaultButton }) => { + const iconName = typeToIcon[type]; + if (!iconName && type === "drag") { + return ; + } else if (!typeToIcon[type]) { + return renderDefaultButton({}); + } else if (renderBtn) { + return renderBtn({ + iconProps: { + iconName: typeToIcon[type], + } + }); + } else { + return ( + + ); + } +}; + +export default FluentUIIcon; diff --git a/packages/fluent/modules/widgets/index.jsx b/packages/fluent/modules/widgets/index.jsx index 6a1a586c9..1b1ccd79f 100644 --- a/packages/fluent/modules/widgets/index.jsx +++ b/packages/fluent/modules/widgets/index.jsx @@ -17,6 +17,7 @@ import FluentUIMultiSelectWidget from "./value/FluentUIMultiSelect"; import FluentUIFieldSelect from "./core/FluentUIFieldSelect"; // core components +import FluentUIIcon from "./core/FluentUIIcon"; import FluentUIButton from "./core/FluentUIButton"; import FluentUIButtonGroup from "./core/FluentUIButtonGroup"; import FluentUIConjs from "./core/FluentUIConjs"; @@ -41,6 +42,7 @@ export default { FluentUIFieldSelect, + FluentUIIcon, FluentUIButton, FluentUIButtonGroup, FluentUIConjs, diff --git a/packages/material/modules/config/index.jsx b/packages/material/modules/config/index.jsx index aa1e44c1d..d8a1d9cf3 100644 --- a/packages/material/modules/config/index.jsx +++ b/packages/material/modules/config/index.jsx @@ -15,6 +15,7 @@ const settings = { renderConjs: (props, {RCE, W: {MaterialConjs}}) => RCE(MaterialConjs, props), renderSwitch: (props, {RCE, W: {MaterialSwitch}}) => RCE(MaterialSwitch, props), renderButton: (props, {RCE, W: {MaterialButton}}) => RCE(MaterialButton, props), + renderIcon: (props, {RCE, W: {MaterialIcon}}) => RCE(MaterialIcon, props), renderButtonGroup: (props, {RCE, W: {MaterialButtonGroup}}) => RCE(MaterialButtonGroup, props), renderValueSources: (props, {RCE, W: {MaterialValueSources}}) => RCE(MaterialValueSources, props), renderFieldSources: (props, {RCE, W: {MaterialValueSources}}) => RCE(MaterialValueSources, props), diff --git a/packages/material/modules/widgets/core/MaterialButton.jsx b/packages/material/modules/widgets/core/MaterialButton.jsx index 1c14e88bc..fdcee6a5c 100644 --- a/packages/material/modules/widgets/core/MaterialButton.jsx +++ b/packages/material/modules/widgets/core/MaterialButton.jsx @@ -1,32 +1,49 @@ import React from "react"; -import DeleteIcon from "@material-ui/icons/Delete"; -import AddIcon from "@material-ui/icons/Add"; import Button from "@material-ui/core/Button"; import IconButton from "@material-ui/core/IconButton"; -export default ({type, label, onClick, readonly, config}) => { - const hideLabelsFor = { - "addRuleGroup": true, - "addRuleGroupExt": true, - }; - const typeToIcon = { - "delGroup": , - "delRuleGroup": , - "delRule": , - "addRuleGroup": , - "addRuleGroupExt": , - "addRule": , - "addGroup": , - }; - const typeToColor = { - "addRule": "default", - "addGroup": "primary", - "delGroup": "secondary", - "delRuleGroup": "secondary", - "delRule": "secondary", +const hideLabelsFor = { + "addRuleGroup": true, + "addRuleGroupExt": true, + "delGroup": true, + "delRuleGroup": true, + "delRule": true, +}; + +const typeToColor = { + "addRule": "primary", + "addGroup": "primary", + "delGroup": "secondary", + "delRuleGroup": "secondary", + "delRule": "secondary", +}; + +export default (props) => { + const {type, label, onClick, readonly, renderIcon} = props; + const iconProps = { + type, + readonly, }; - if (!label || hideLabelsFor[type]) - return {typeToIcon[type]}; - else - return ; + const icon = renderIcon?.(iconProps); + + if (!label || hideLabelsFor[type]) { + return ( + {icon} + ); + } else { + return ( + + ); + } }; diff --git a/packages/material/modules/widgets/core/MaterialIcon.jsx b/packages/material/modules/widgets/core/MaterialIcon.jsx new file mode 100644 index 000000000..8c0ae3760 --- /dev/null +++ b/packages/material/modules/widgets/core/MaterialIcon.jsx @@ -0,0 +1,46 @@ +import React from "react"; +import DeleteIcon from "@material-ui/icons/Delete"; +import AddIcon from "@material-ui/icons/Add"; +import DragHandle from "@material-ui/icons/DragHandle"; +import Icon from "@material-ui/core/Icon"; +import { Utils } from "@react-awesome-query-builder/ui"; +const { DragIcon } = Utils; + +const typeToIcon = { + "delGroup": , + "delRuleGroup": , + "delRule": , + "addRuleGroup": , + "addRuleGroupExt": , + "addRule": , + "addGroup": , + "drag": , +}; + +const typeToColor = { + // "addRule": "primary", + // "addGroup": "primary", + // "delGroup": "secondary", + // "delRuleGroup": "secondary", + // "delRule": "secondary", + "drag": "inherit", +}; + +export default ({type, readonly}) => { + let icon = typeToIcon[type]; + if (!icon && type === "drag") { + return ; + } + + if (type === "drag") { + return ( + {icon} + ); + } else { + return icon; + } +}; diff --git a/packages/material/modules/widgets/index.jsx b/packages/material/modules/widgets/index.jsx index 7c8ee83d6..c15e09f9d 100644 --- a/packages/material/modules/widgets/index.jsx +++ b/packages/material/modules/widgets/index.jsx @@ -24,6 +24,7 @@ import MaterialFieldSelect from "./core/MaterialFieldSelect"; import MaterialFieldAutocomplete from "./core/MaterialFieldAutocomplete"; // core components +import MaterialIcon from "./core/MaterialIcon"; import MaterialButton from "./core/MaterialButton"; import MaterialButtonGroup from "./core/MaterialButtonGroup"; import MaterialConjs from "./core/MaterialConjs"; @@ -75,6 +76,7 @@ export default { MaterialFieldSelect, MaterialFieldAutocomplete, + MaterialIcon, MaterialButton, MaterialButtonGroup, MaterialConjs, diff --git a/packages/mui/modules/config/index.jsx b/packages/mui/modules/config/index.jsx index 8bb912adf..71d250349 100644 --- a/packages/mui/modules/config/index.jsx +++ b/packages/mui/modules/config/index.jsx @@ -14,6 +14,7 @@ const settings = { renderConjs: (props, {RCE, W: {MuiConjs}}) => RCE(MuiConjs, props), renderSwitch: (props, {RCE, W: {MuiSwitch}}) => RCE(MuiSwitch, props), renderButton: (props, {RCE, W: {MuiButton}}) => RCE(MuiButton, props), + renderIcon: (props, {RCE, W: {MuiIcon}}) => RCE(MuiIcon, props), renderButtonGroup: (props, {RCE, W: {MuiButtonGroup}}) => RCE(MuiButtonGroup, props), renderValueSources: (props, {RCE, W: {MuiValueSources}}) => RCE(MuiValueSources, props), renderFieldSources: (props, {RCE, W: {MuiValueSources}}) => RCE(MuiValueSources, props), diff --git a/packages/mui/modules/widgets/core/MuiButton.jsx b/packages/mui/modules/widgets/core/MuiButton.jsx index aaa974ee7..1f5fb88de 100644 --- a/packages/mui/modules/widgets/core/MuiButton.jsx +++ b/packages/mui/modules/widgets/core/MuiButton.jsx @@ -1,30 +1,31 @@ import React from "react"; -import DeleteIcon from "@mui/icons-material/Delete"; -import AddIcon from "@mui/icons-material/Add"; import Button from "@mui/material/Button"; import IconButton from "@mui/material/IconButton"; -export default ({type, label, onClick, readonly, config}) => { - const hideLabelsFor = { - "addRuleGroup": true, - "addRuleGroupExt": true, - }; - const typeToIcon = { - "delGroup": , - "delRuleGroup": , - "delRule": , - "addRule": , - "addGroup": , - "addRuleGroupExt": , - "addRuleGroup": , - }; - const typeToColor = { - "addRule": "neutral", - "addGroup": "primary", - "delGroup": "secondary", - "delRuleGroup": "secondary", - "delRule": "secondary", +const hideLabelsFor = { + "addRuleGroup": true, + "addRuleGroupExt": true, + "delGroup": true, + "delRuleGroup": true, + "delRule": true, +}; + +const typeToColor = { + "addRule": "neutral", + "addGroup": "primary", + "delGroup": "secondary", + "delRuleGroup": "secondary", + "delRule": "secondary", +}; + +export default (props) => { + const {type, label, onClick, readonly, renderIcon} = props; + const iconProps = { + type, + readonly, }; + const icon = renderIcon?.(iconProps); + if (!label || hideLabelsFor[type]) { return ( { disabled={readonly} onClick={onClick} color={typeToColor[type]} - >{typeToIcon[type]} + >{icon} ); } else { return ( @@ -41,7 +42,7 @@ export default ({type, label, onClick, readonly, config}) => { disabled={readonly} onClick={onClick} color={typeToColor[type]} - startIcon={typeToIcon[type]} + startIcon={icon} >{label} ); } diff --git a/packages/mui/modules/widgets/core/MuiIcon.jsx b/packages/mui/modules/widgets/core/MuiIcon.jsx new file mode 100644 index 000000000..aee7aeccc --- /dev/null +++ b/packages/mui/modules/widgets/core/MuiIcon.jsx @@ -0,0 +1,46 @@ +import React from "react"; +import DragHandle from "@mui/icons-material/DragHandle"; +import DeleteIcon from "@mui/icons-material/Delete"; +import AddIcon from "@mui/icons-material/Add"; +import Icon from "@mui/material/Icon"; +import { Utils } from "@react-awesome-query-builder/ui"; +const { DragIcon } = Utils; + +const typeToIcon = { + "delGroup": , + "delRuleGroup": , + "delRule": , + "addRuleGroup": , + "addRuleGroupExt": , + "addRule": , + "addGroup": , + "drag": , +}; + +const typeToColor = { + // "addRule": "default", + // "addGroup": "primary", + // "delGroup": "secondary", + // "delRuleGroup": "secondary", + // "delRule": "secondary", + "drag": "default", +}; + +export default ({type, readonly}) => { + let icon = typeToIcon[type]; + if (!icon && type === "drag") { + return ; + } + + if (type === "drag") { + return ( + {icon} + ); + } else { + return icon; + } +}; diff --git a/packages/mui/modules/widgets/index.jsx b/packages/mui/modules/widgets/index.jsx index f20dfcc57..ce1b7c853 100644 --- a/packages/mui/modules/widgets/index.jsx +++ b/packages/mui/modules/widgets/index.jsx @@ -26,6 +26,7 @@ import MuiFieldSelect from "./core/MuiFieldSelect"; import MuiFieldAutocomplete from "./core/MuiFieldAutocomplete"; // core components +import MuiIcon from "./core/MuiIcon"; import MuiButton from "./core/MuiButton"; import MuiButtonGroup from "./core/MuiButtonGroup"; import MuiConjs from "./core/MuiConjs"; @@ -90,6 +91,7 @@ export default { MuiFieldSelect, MuiFieldAutocomplete, + MuiIcon, MuiButton, MuiButtonGroup, MuiConjs, diff --git a/packages/tests/specs/Compress.test.ts b/packages/tests/specs/Compress.test.ts index 385714fd9..819feecc8 100644 --- a/packages/tests/specs/Compress.test.ts +++ b/packages/tests/specs/Compress.test.ts @@ -174,6 +174,7 @@ describe("settings.useConfigCompress", () => { ignoreLog: (errText) => { return errText.includes("The tag <%s> is unrecognized in this browser") && errText.includes("slidermark_notexists") || errText.includes("Invalid DOM property `%s`") && errText.includes("readonly") + || errText.includes("React does not recognize the `%s` prop") && errText.includes("renderIcon") ; } }); diff --git a/packages/ui/modules/components/item/Group.jsx b/packages/ui/modules/components/item/Group.jsx index 5ee09b62d..a4cae9b81 100644 --- a/packages/ui/modules/components/item/Group.jsx +++ b/packages/ui/modules/components/item/Group.jsx @@ -7,7 +7,7 @@ import Draggable from "../containers/Draggable"; import classNames from "classnames"; import { Item } from "./Item"; import {GroupActions} from "./GroupActions"; -import {WithConfirmFn, DragIcon, dummyFn} from "../utils"; +import {WithConfirmFn, dummyFn} from "../utils"; const {isEmptyGroupChildren} = Utils.RuleUtils; const defaultPosition = "topRight"; @@ -267,13 +267,17 @@ export class BasicGroup extends Component { renderDrag() { const { handleDraggerMouseDown } = this.props; - const drag = this.showDragIcon() - && ; - return drag; + const { config } = this.props; + const { renderIcon } = config.settings; + const Icon = (pr) => renderIcon?.(pr, config.ctx); + const icon = ; + return this.showDragIcon() && (
{icon}
); } conjunctionOptions() { diff --git a/packages/ui/modules/components/item/GroupActions.jsx b/packages/ui/modules/components/item/GroupActions.jsx index 8efd55bcb..6287bb36c 100644 --- a/packages/ui/modules/components/item/GroupActions.jsx +++ b/packages/ui/modules/components/item/GroupActions.jsx @@ -20,9 +20,10 @@ export class GroupActions extends PureComponent { } = this.props; const { immutableGroupsMode, addRuleLabel, addGroupLabel, delGroupLabel, groupActionsPosition, - renderButton, renderSwitch, renderButtonGroup, + renderButton, renderIcon, renderSwitch, renderButtonGroup, lockLabel, lockedLabel, showLock, canDeleteLocked, } = config.settings; + const Icon = (pr) => renderIcon(pr, config.ctx); const Btn = (pr) => renderButton(pr, config.ctx); const Switch = (pr) => renderSwitch(pr, config.ctx); const BtnGrp = (pr) => renderButtonGroup(pr, config.ctx); @@ -33,13 +34,13 @@ export class GroupActions extends PureComponent { />; const addRuleBtn = !immutableGroupsMode && canAddRule && !isLocked && ; const addGroupBtn = !immutableGroupsMode && canAddGroup && !isLocked && ; const delGroupBtn = !immutableGroupsMode && canDeleteGroup && (!isLocked || isLocked && canDeleteLocked) && ; return ( diff --git a/packages/ui/modules/components/item/Rule.jsx b/packages/ui/modules/components/item/Rule.jsx index 646f6440e..802b8a895 100644 --- a/packages/ui/modules/components/item/Rule.jsx +++ b/packages/ui/modules/components/item/Rule.jsx @@ -8,7 +8,7 @@ import FieldWrapper from "../rule/FieldWrapper"; import Widget from "../rule/Widget"; import OperatorOptions from "../rule/OperatorOptions"; import {useOnPropsChanged} from "../../utils/reactUtils"; -import {Col, DragIcon, dummyFn, WithConfirmFn} from "../utils"; +import {Col, dummyFn, WithConfirmFn} from "../utils"; import classNames from "classnames"; const {getFieldConfig, getOperatorConfig, getFieldWidgetConfig, getFieldParts} = Utils.ConfigUtils; const {isEmptyRuleProperties} = Utils.RuleUtils; @@ -270,14 +270,19 @@ class Rule extends Component { } renderDrag() { + const { handleDraggerMouseDown } = this.props; + const { config } = this.props; const { showDragIcon } = this.meta; - - return showDragIcon - && ; + const { renderIcon } = config.settings; + const Icon = (pr) => renderIcon?.(pr, config.ctx); + const icon = ; + return showDragIcon && (
{icon}
); } renderDel() { @@ -286,13 +291,15 @@ class Rule extends Component { deleteLabel, immutableGroupsMode, renderButton, + renderIcon, canDeleteLocked } = config.settings; + const Icon = (pr) => renderIcon(pr, config.ctx); const Btn = (pr) => renderButton(pr, config.ctx); return !immutableGroupsMode && (!isLocked || isLocked && canDeleteLocked) && ( ); } diff --git a/packages/ui/modules/components/item/RuleGroupActions.jsx b/packages/ui/modules/components/item/RuleGroupActions.jsx index 44888e3fc..80b8cc3b1 100644 --- a/packages/ui/modules/components/item/RuleGroupActions.jsx +++ b/packages/ui/modules/components/item/RuleGroupActions.jsx @@ -9,9 +9,10 @@ export class RuleGroupActions extends PureComponent { } = this.props; const { immutableGroupsMode, addRuleLabel, delGroupLabel, - renderButton, renderSwitch, renderButtonGroup, + renderButton, renderIcon, renderSwitch, renderButtonGroup, lockLabel, lockedLabel, showLock, canDeleteLocked, } = config.settings; + const Icon = (pr) => renderIcon(pr, config.ctx); const Btn = (pr) => renderButton(pr, config.ctx); const Switch = (pr) => renderSwitch(pr, config.ctx); const BtnGrp = (pr) => renderButtonGroup(pr, config.ctx); @@ -21,11 +22,11 @@ export class RuleGroupActions extends PureComponent { />; const addRuleBtn = !immutableGroupsMode && canAddRule && !isLocked && ; const delGroupBtn = !immutableGroupsMode && canDeleteGroup && (!isLocked || isLocked && canDeleteLocked) && ; return ( diff --git a/packages/ui/modules/components/item/RuleGroupExtActions.jsx b/packages/ui/modules/components/item/RuleGroupExtActions.jsx index 4e30a96b7..f2eb2f805 100644 --- a/packages/ui/modules/components/item/RuleGroupExtActions.jsx +++ b/packages/ui/modules/components/item/RuleGroupExtActions.jsx @@ -9,9 +9,10 @@ export class RuleGroupExtActions extends PureComponent { } = this.props; const { immutableGroupsMode, addSubRuleLabel, delGroupLabel, - renderButton, renderSwitch, renderButtonGroup, + renderButton, renderIcon, renderSwitch, renderButtonGroup, lockLabel, lockedLabel, showLock, canDeleteLocked, } = config.settings; + const Icon = (pr) => renderIcon(pr, config.ctx); const Btn = (pr) => renderButton(pr, config.ctx); const Switch = (pr) => renderSwitch(pr, config.ctx); const BtnGrp = (pr) => renderButtonGroup(pr, config.ctx); @@ -21,11 +22,11 @@ export class RuleGroupExtActions extends PureComponent { />; const addRuleBtn = !immutableGroupsMode && canAddRule && !isLocked && ; const delGroupBtn = !immutableGroupsMode && canDeleteGroup && (!isLocked || isLocked && canDeleteLocked) && ; return ( diff --git a/packages/ui/modules/components/item/SwitchGroupActions.jsx b/packages/ui/modules/components/item/SwitchGroupActions.jsx index 94c73b61f..c37d918bf 100644 --- a/packages/ui/modules/components/item/SwitchGroupActions.jsx +++ b/packages/ui/modules/components/item/SwitchGroupActions.jsx @@ -19,9 +19,10 @@ export class SwitchGroupActions extends PureComponent { } = this.props; const { immutableGroupsMode, addCaseLabel, addDefaultCaseLabel, groupActionsPosition, - renderButton, renderSwitch, renderButtonGroup, + renderButton, renderIcon, renderSwitch, renderButtonGroup, lockLabel, lockedLabel, showLock, } = config.settings; + const Icon = (pr) => renderIcon(pr, config.ctx); const Btn = (pr) => renderButton(pr, config.ctx); const Switch = (pr) => renderSwitch(pr, config.ctx); const BtnGrp = (pr) => renderButtonGroup(pr, config.ctx); @@ -32,11 +33,11 @@ export class SwitchGroupActions extends PureComponent { />; const addCaseGroupBtn = !immutableGroupsMode && canAddGroup && !isLocked && ; const addDefaultCaseGroupBtn = !immutableGroupsMode && canAddDefault && !isLocked && ; return ( diff --git a/packages/ui/modules/components/widgets/vanilla/core/VanillaButton.jsx b/packages/ui/modules/components/widgets/vanilla/core/VanillaButton.jsx index 5e89fed46..87a5548b9 100644 --- a/packages/ui/modules/components/widgets/vanilla/core/VanillaButton.jsx +++ b/packages/ui/modules/components/widgets/vanilla/core/VanillaButton.jsx @@ -1,13 +1,14 @@ import React from "react"; -export default ({type, label, onClick, readonly, config}) => { - const typeToLabel = { - "addRuleGroup": "+", - "addRuleGroupExt": "+", - "delGroup": "x", - "delRuleGroup": "x", - "delRule": "x", - }; +const typeToLabel = { + "addRuleGroup": "+", + "addRuleGroupExt": "+", + "delGroup": "x", + "delRuleGroup": "x", + "delRule": "x", +}; + +export default ({type, label, onClick, readonly}) => { const btnLabel = label || typeToLabel[type]; return ; }; diff --git a/packages/ui/modules/components/widgets/vanilla/core/VanillaIcon.jsx b/packages/ui/modules/components/widgets/vanilla/core/VanillaIcon.jsx new file mode 100644 index 000000000..0ff4dbe27 --- /dev/null +++ b/packages/ui/modules/components/widgets/vanilla/core/VanillaIcon.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { DragIcon } from "../../../utils"; + +export default ({type}) => { + const typeToIcon = { + }; + let icon = typeToIcon[type]; + if (!icon && type === "drag") { + icon = ; + } + + return icon || null; +}; diff --git a/packages/ui/modules/components/widgets/vanilla/index.jsx b/packages/ui/modules/components/widgets/vanilla/index.jsx index 2cac8cbf5..21210ab32 100644 --- a/packages/ui/modules/components/widgets/vanilla/index.jsx +++ b/packages/ui/modules/components/widgets/vanilla/index.jsx @@ -18,6 +18,7 @@ export {default as VanillaFieldSelect} from "./core/VanillaFieldSelect"; // core components export {default as VanillaConjs} from "./core/VanillaConjs"; export {default as VanillaButton} from "./core/VanillaButton"; +export {default as VanillaIcon} from "./core/VanillaIcon"; export {default as VanillaButtonGroup} from "./core/VanillaButtonGroup"; export {default as VanillaValueSources} from "./core/VanillaValueSources"; export {default as VanillaSwitch} from "./core/VanillaSwitch"; diff --git a/packages/ui/modules/config/index.jsx b/packages/ui/modules/config/index.jsx index 11c4724a6..e11449337 100644 --- a/packages/ui/modules/config/index.jsx +++ b/packages/ui/modules/config/index.jsx @@ -119,6 +119,7 @@ const settings = { renderConjs: (props, {RCE, W: {VanillaConjs}}) => RCE(VanillaConjs, props), renderSwitch: (props, {RCE, W: {VanillaSwitch}}) => RCE(VanillaSwitch, props), renderButton: (props, {RCE, W: {VanillaButton}}) => RCE(VanillaButton, props), + renderIcon: (props, {RCE, W: {VanillaIcon}}) => RCE(VanillaIcon, props), renderButtonGroup: (props, {RCE, W: {VanillaButtonGroup}}) => RCE(VanillaButtonGroup, props), renderProvider: (props, {RCE, W: {VanillaProvider}}) => RCE(VanillaProvider, props), renderValueSources: (props, {RCE, W: {VanillaValueSources}}) => RCE(VanillaValueSources, props), diff --git a/packages/ui/modules/index.d.ts b/packages/ui/modules/index.d.ts index 07fe6c3ff..cb785cb1b 100644 --- a/packages/ui/modules/index.d.ts +++ b/packages/ui/modules/index.d.ts @@ -195,14 +195,24 @@ export type Query = ElementType; // Props for render* in RenderSettings ///////////////// +type ButtonIconType = "addRule" | "addGroup" | "delRule" | "delGroup" | "addRuleGroup" | "delRuleGroup"; +type IconType = ButtonIconType | "drag"; + export interface ButtonProps { - type: "addRule" | "addGroup" | "delRule" | "delGroup" | "addRuleGroup" | "delRuleGroup", - onClick(): void, + type: ButtonIconType, + renderIcon?: FactoryWithContext, + onClick(): void, label: string, config?: Config, readonly?: boolean, } +export interface IconProps { + type: IconType; + config?: Config, + readonly?: boolean, +} + export interface SwitchProps { value: boolean, setValue(newValue?: boolean): void, @@ -296,6 +306,7 @@ export interface RenderSettings { renderFunc?: FactoryWithContext | SerializedFunction, renderConjs?: FactoryWithContext | SerializedFunction, renderButton?: FactoryWithContext | SerializedFunction, + renderIcon?: FactoryWithContext | SerializedFunction, renderButtonGroup?: FactoryWithContext | SerializedFunction, renderSwitch?: FactoryWithContext | SerializedFunction, renderProvider?: FactoryWithContext | SerializedFunction, diff --git a/packages/ui/modules/utils/index.js b/packages/ui/modules/utils/index.js index 741509d58..b25bd40a7 100644 --- a/packages/ui/modules/utils/index.js +++ b/packages/ui/modules/utils/index.js @@ -1,3 +1,4 @@ import { Utils } from "@react-awesome-query-builder/core"; import * as ReactUtils from "./reactUtils"; -export default { ...Utils, ReactUtils }; +import { DragIcon } from "../components/utils"; +export default { ...Utils, ReactUtils, DragIcon };