From 7b5ea7e527dc64f8c8fa77df52ee687a70f3a31c Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Fri, 15 Sep 2023 10:31:15 +0200 Subject: [PATCH] [IOAPPFD0-144] Add the new `IconContained` component (#62) ## Short description This PR adds the new `IconContained`, a component designed to render an icon inside a circle. Inspired by [Google Material Icon Button's various contained styles](https://m3.material.io/components/icon-buttons/specs), the component takes two props besides `icon`: `variant` and `color`. The default and unique values, for now, are `tonal` (variant) and `neutral` (color). > [!WARNING] > `IconContained` is just a special wrapper for the `Icon` component. It's also not an interactive component, unlike the Google Material's `IconButton`. When adding new styles, you should be aware of this context and be careful not to add variants that look like interactive counterparts. ## List of changes proposed in this pull request - Add the new `IconContained` component - **[EXTRA]** Add the new `transactions` icon ### Preview ## How to test Go to the **Icons (Foundation)** in the example app. --------- Co-authored-by: Cristiano Tofani --- example/src/pages/Icons.tsx | 23 +++++- src/components/icons/Icon.tsx | 4 +- src/components/icons/IconContained.tsx | 76 +++++++++++++++++++ src/components/icons/index.tsx | 1 + src/components/icons/svg/IconTransactions.tsx | 2 +- .../icons/svg/IconTransactionsBoxed.tsx | 16 ++++ .../icons/svg/originals/IconTransactions.svg | 8 ++ .../svg/originals/IconTransactionsBoxed.svg | 8 ++ src/core/IOStyles.ts | 4 +- 9 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 src/components/icons/IconContained.tsx create mode 100644 src/components/icons/svg/IconTransactionsBoxed.tsx create mode 100644 src/components/icons/svg/originals/IconTransactions.svg create mode 100644 src/components/icons/svg/originals/IconTransactionsBoxed.svg diff --git a/example/src/pages/Icons.tsx b/example/src/pages/Icons.tsx index 04f74eb0..dcfbd092 100644 --- a/example/src/pages/Icons.tsx +++ b/example/src/pages/Icons.tsx @@ -1,6 +1,8 @@ import { H2, H3, + H4, + HSpacer, IOBiometricIcons, IOCategoryIcons, IOColors, @@ -8,16 +10,19 @@ import { IOIcons, IONavIcons, IOProductIcons, + IOStyles, IOSystemIcons, IOThemeContext, IOVisualCostants, Icon, + IconContained, SVGIconProps } from "@pagopa/io-app-design-system"; import React, { useContext } from "react"; import { StyleSheet, View } from "react-native"; import { IconViewerBox, iconItemGutter } from "../components/IconViewerBox"; import { Screen } from "../components/Screen"; +import { ComponentViewerBox } from "../components/ComponentViewerBox"; // Filter the main object, removing already displayed icons in the other sets type IconSubsetObject = Record< @@ -208,7 +213,23 @@ export const Icons = () => { /> ))} -

+ +

+ IconContained +

+ + + + + + + + +

Sizes

diff --git a/src/components/icons/Icon.tsx b/src/components/icons/Icon.tsx index fdbd3eb6..c75a2a6c 100644 --- a/src/components/icons/Icon.tsx +++ b/src/components/icons/Icon.tsx @@ -139,6 +139,7 @@ import IconSystemSettingsAndroid from "./svg/IconSystemSettingsAndroid"; import IconSystemSettingsiOS from "./svg/IconSystemSettingsiOS"; import IconSystemToggleInstructions from "./svg/IconSystemToggleInstructions"; import IconTag from "./svg/IconTag"; +import IconTransactionsBoxed from "./svg/IconTransactionsBoxed"; import IconTransactions from "./svg/IconTransactions"; import IconTrashcan from "./svg/IconTrashcan"; import IconWarningFilled from "./svg/IconWarningFilled"; @@ -166,6 +167,7 @@ export const IOIcons = { copy: IconCopy, selfCert: IconSelfCertification, institution: IconInstitution, + merchant: IconMerchant, hourglass: IconHourglass, shareiOs: IconShareiOs, shareAndroid: IconShareAndroid, @@ -203,6 +205,7 @@ export const IOIcons = { fiscalCodeIndividual: IconFiscalCodeIndividual, creditCard: IconCreditCard, bonus: IconBonus, + transactionsBoxed: IconTransactionsBoxed, transactions: IconTransactions, amount: IconAmount, psp: IconPSP, @@ -296,7 +299,6 @@ export const IOIcons = { productIOAppBlueBg: IconProductIOAppBlueBg, checkTick: IconCheckTick, checkTickBig: IconCheckTickBig, - merchant: IconMerchant, light: IconLight, lightFilled: IconLightFilled, systemSettingsAndroid: IconSystemSettingsAndroid, diff --git a/src/components/icons/IconContained.tsx b/src/components/icons/IconContained.tsx new file mode 100644 index 00000000..c9b593b0 --- /dev/null +++ b/src/components/icons/IconContained.tsx @@ -0,0 +1,76 @@ +import * as React from "react"; +import { useMemo } from "react"; +import { StyleSheet, View } from "react-native"; +import { IOVisualCostants, IOColors } from "../../core"; +import { IOIcons, Icon } from "."; + +type IconContained = { + variant: "tonal"; + color: "neutral"; + icon: IOIcons; +}; + +type IconContainedVisualAttrs = { + background: IOColors; + foreground: IOColors; +}; + +type IconContainedColorVariants = Record< + IconContained["color"], + IconContainedVisualAttrs +>; + +const tonalColorMap: IconContainedColorVariants = { + neutral: { + background: "blueIO-50", + foreground: "grey-450" + } +}; + +const variantMap: Record = + { + tonal: tonalColorMap + }; + +const IconContainedDefaultSize = IOVisualCostants.iconContainedSizeDefault; + +const styles = StyleSheet.create({ + iconContainedWrapper: { + overflow: "hidden", + display: "flex", + alignItems: "center", + justifyContent: "center", + width: IconContainedDefaultSize, + height: IconContainedDefaultSize, + borderRadius: IconContainedDefaultSize / 2 + } +}); + +/* +`IconContained` is just a special wrapper for the `Icon` component. +It's also not an interactive component, unlike the `IconButton`. +When adding new styles, you should be aware of this context and be careful +not to add variants that look like interactive counterparts. +*/ +export const IconContained = ({ variant, color, icon }: IconContained) => { + const backgroundColor = useMemo( + () => variantMap[variant][color].background, + [variant, color] + ); + + const foregroundColor = useMemo( + () => variantMap[variant][color].foreground, + [variant, color] + ); + + return ( + + + + ); +}; diff --git a/src/components/icons/index.tsx b/src/components/icons/index.tsx index 998237c4..1aecc35a 100644 --- a/src/components/icons/index.tsx +++ b/src/components/icons/index.tsx @@ -1 +1,2 @@ export * from "./Icon"; +export * from "./IconContained"; diff --git a/src/components/icons/svg/IconTransactions.tsx b/src/components/icons/svg/IconTransactions.tsx index 816608d9..d5588301 100644 --- a/src/components/icons/svg/IconTransactions.tsx +++ b/src/components/icons/svg/IconTransactions.tsx @@ -7,7 +7,7 @@ const IconTransactions = ({ size, style, ...props }: SVGIconProps) => ( diff --git a/src/components/icons/svg/IconTransactionsBoxed.tsx b/src/components/icons/svg/IconTransactionsBoxed.tsx new file mode 100644 index 00000000..f7ba5421 --- /dev/null +++ b/src/components/icons/svg/IconTransactionsBoxed.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Svg, Path } from "react-native-svg"; +import { SVGIconProps } from "../Icon"; + +const IconTransactionsBoxed = ({ size, style, ...props }: SVGIconProps) => ( + + + +); + +export default IconTransactionsBoxed; diff --git a/src/components/icons/svg/originals/IconTransactions.svg b/src/components/icons/svg/originals/IconTransactions.svg new file mode 100644 index 00000000..3a027d2b --- /dev/null +++ b/src/components/icons/svg/originals/IconTransactions.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/components/icons/svg/originals/IconTransactionsBoxed.svg b/src/components/icons/svg/originals/IconTransactionsBoxed.svg new file mode 100644 index 00000000..a97fab83 --- /dev/null +++ b/src/components/icons/svg/originals/IconTransactionsBoxed.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/core/IOStyles.ts b/src/core/IOStyles.ts index cdd7f47d..3db06ad7 100644 --- a/src/core/IOStyles.ts +++ b/src/core/IOStyles.ts @@ -14,13 +14,15 @@ interface IOVisualCostants { // Dimensions avatarSizeSmall: number; avatarSizeMedium: number; + iconContainedSizeDefault: number; } export const IOVisualCostants: IOVisualCostants = { appMarginDefault: 24, headerHeight: 56, avatarSizeSmall: 44, - avatarSizeMedium: 66 + avatarSizeMedium: 66, + iconContainedSizeDefault: 44 }; export const IOStyles = StyleSheet.create({