diff --git a/packages/bento-design-system/src/Modal/Modal.tsx b/packages/bento-design-system/src/Modal/Modal.tsx index f78a1ccc7..97b4813ce 100644 --- a/packages/bento-design-system/src/Modal/Modal.tsx +++ b/packages/bento-design-system/src/Modal/Modal.tsx @@ -38,9 +38,13 @@ type Props = { size?: ModalSize; kind?: ModalKind; autoFocus?: boolean; + portalContainer?: HTMLElement; }; -type CustomModalProps = Pick & { +type CustomModalProps = Pick< + Props, + "children" | "isDestructive" | "size" | "autoFocus" | "portalContainer" +> & { ["aria-label"]: string; }; @@ -48,7 +52,7 @@ export function CustomModal(props: CustomModalProps) { const config = useBentoConfig().modal; const ref = useRef(null); const { overlayProps, underlayProps } = useOverlay({ ...props, isOpen: true }, ref); - const createPortal = useCreatePortal(); + const createPortal = useCreatePortal(props.portalContainer); usePreventScroll(); diff --git a/packages/bento-design-system/src/util/useCreatePortal.tsx b/packages/bento-design-system/src/util/useCreatePortal.tsx index 676e2e83c..9423a5c7b 100644 --- a/packages/bento-design-system/src/util/useCreatePortal.tsx +++ b/packages/bento-design-system/src/util/useCreatePortal.tsx @@ -4,11 +4,13 @@ import { Children } from "./Children"; import { useIsSSR } from "@react-aria/ssr"; import { BentoThemePortalProvider } from "../BentoThemeContext"; -export function useCreatePortal(): (children: Children) => ReactPortal | null { +export function useCreatePortal( + portalContainer?: HTMLElement +): (children: Children) => ReactPortal | null { const isSSR = useIsSSR(); return (children) => { const content = {children}; - return isSSR ? null : createReactPortal(content, document.body); + return isSSR ? null : createReactPortal(content, portalContainer ?? document.body); }; } diff --git a/packages/configuration-builder/src/TokensSection/InteractiveElementsTokens.tsx b/packages/configuration-builder/src/TokensSection/InteractiveElementsTokens.tsx index 7caeb9b00..85df13249 100644 --- a/packages/configuration-builder/src/TokensSection/InteractiveElementsTokens.tsx +++ b/packages/configuration-builder/src/TokensSection/InteractiveElementsTokens.tsx @@ -15,7 +15,7 @@ import { useTranslation } from "react-i18next"; import { Playground as _Playground } from "./Playground"; import { ThemeConfig } from "../ConfiguratorStatusContext"; import { useConfiguredTheme } from "../utils/preview"; -import { ColorKey, ColorToken, stepNames } from "../utils/paletteUtils"; +import { getPaletteStep, getRelativeStep } from "../utils/paletteUtils"; type Props = { tokens: Pick< @@ -128,38 +128,6 @@ function Playground({ hierarchy }: { hierarchy: ButtonProps["hierarchy"] }) { ); } -function getRelativeStep(colorToken: ColorToken, gap: number): ColorToken { - if (colorToken.colorKey === "black" || colorToken.colorKey === "white") { - return { colorKey: "black", alpha: colorToken.alpha }; - } - const [palette, step] = colorToken.colorKey.split("-"); - const stepIndex = stepNames.indexOf(step as (typeof stepNames)[number]); - const nextStepIndex = stepIndex + gap; - if (stepNames[nextStepIndex] != null) { - return { - colorKey: `${palette}-${stepNames[nextStepIndex]}` as ColorKey, - alpha: colorToken.alpha, - }; - } else { - return { colorKey: "black", alpha: colorToken.alpha }; - } -} - -function getPaletteStep( - colorKey: ColorKey, - step: (typeof stepNames)[number], - alpha: number -): ColorToken { - if (colorKey === "black" || colorKey === "white") { - return { colorKey: "black", alpha }; - } - const [palette] = colorKey.split("-"); - return { - colorKey: `${palette}-${step}` as ColorKey, - alpha, - }; -} - function capitalizeFirstLetter(value: string) { return value.charAt(0).toUpperCase() + value.slice(1); } diff --git a/packages/configuration-builder/src/TokensSection/OtherTokens.tsx b/packages/configuration-builder/src/TokensSection/OtherTokens.tsx index 505dbf738..0e29c01d2 100644 --- a/packages/configuration-builder/src/TokensSection/OtherTokens.tsx +++ b/packages/configuration-builder/src/TokensSection/OtherTokens.tsx @@ -1,8 +1,22 @@ import { useTranslation } from "react-i18next"; import { ThemeConfig } from "../ConfiguratorStatusContext"; import { Playground as _Playground } from "./Playground"; -import { Column, Columns, Title, Stack, Box, Body, Link } from "@buildo/bento-design-system"; +import { + Column, + Columns, + Title, + Stack, + Box, + Body, + Link, + Modal, + unsafeLocalizedString, + withBentoTheme, +} from "@buildo/bento-design-system"; import { ColorSelector } from "./ColorSelector"; +import { useRef } from "react"; +import { getRelativeStep } from "../utils/paletteUtils"; +import { useConfiguredTheme } from "../utils/preview"; type Props = { tokens: Pick< @@ -20,9 +34,31 @@ type Props = { function ModalPlayground() { const { t } = useTranslation(); + const ExampleModal = withBentoTheme( + { + backgroundColor: { + backgroundDarkScrim: "transparent", + }, + }, + Modal + ); + + const containerRef = useRef(null); + return ( <_Playground> - + {t("LoremIpsum.longText")} @@ -30,6 +66,23 @@ function ModalPlayground() { + {}} + portalContainer={containerRef.current ?? undefined} + primaryAction={{ + label: unsafeLocalizedString(t("TokensSection.Step.other.modalAction")), + onPress: () => {}, + }} + secondaryAction={{ + label: unsafeLocalizedString(t("TokensSection.Step.other.modalCancel")), + onPress: () => {}, + }} + autoFocus={false} + > + {t("TokensSection.Step.other.modalContent")} + ); @@ -37,6 +90,25 @@ function ModalPlayground() { function LinkPlayground() { const { t } = useTranslation(); + const theme = useConfiguredTheme(); + + const HoverLink = withBentoTheme( + { + interactiveForegroundColor: { + linkEnabled: theme.interactiveForegroundColor?.linkHover, + }, + }, + Link + ); + + const FocusLink = withBentoTheme( + { + interactiveForegroundColor: { + linkEnabled: theme.interactiveForegroundColor?.linkFocus, + }, + }, + Link + ); return ( <_Playground> @@ -50,9 +122,9 @@ function LinkPlayground() { > - - - + + + - - - + + + @@ -145,6 +217,8 @@ export function OtherTokens(props: Props) { interactiveForegroundColor: { ...props.tokens.interactiveForegroundColor, linkEnabled: value, + linkHover: getRelativeStep(value, 2), + linkFocus: getRelativeStep(value, 2), }, }) } diff --git a/packages/configuration-builder/src/locales/en.json b/packages/configuration-builder/src/locales/en.json index 2be820fcd..986b7e530 100644 --- a/packages/configuration-builder/src/locales/en.json +++ b/packages/configuration-builder/src/locales/en.json @@ -164,6 +164,10 @@ "TokensSection.Step.inputs.label": "Label", "TokensSection.Step.inputs.placeholder": "Placeholder", "TokensSection.Step.other": "Other", + "TokensSection.Step.other.modalTitle": "Title", + "TokensSection.Step.other.modalContent": "Modal content.", + "TokensSection.Step.other.modalAction": "Action", + "TokensSection.Step.other.modalCancel": "Cancel", "TokensSection.Step.other.linkEnabled": "Link Enabled", "TokensSection.Step.other.linkHover": "Link Hover", "TokensSection.Step.other.linkFocus": "Link Focus", diff --git a/packages/configuration-builder/src/utils/paletteUtils.ts b/packages/configuration-builder/src/utils/paletteUtils.ts index 342920ad3..8fee41d3f 100644 --- a/packages/configuration-builder/src/utils/paletteUtils.ts +++ b/packages/configuration-builder/src/utils/paletteUtils.ts @@ -108,3 +108,35 @@ export function colorTokenToRGBA(colors: ThemeConfig["colors"]) { export function colorToken(colorKey: ColorKey, alpha?: number): ColorToken { return { colorKey, alpha: alpha ?? 100 }; } + +export function getRelativeStep(colorToken: ColorToken, gap: number): ColorToken { + if (colorToken.colorKey === "black" || colorToken.colorKey === "white") { + return { colorKey: "black", alpha: colorToken.alpha }; + } + const [palette, step] = colorToken.colorKey.split("-"); + const stepIndex = stepNames.indexOf(step as (typeof stepNames)[number]); + const nextStepIndex = stepIndex + gap; + if (stepNames[nextStepIndex] != null) { + return { + colorKey: `${palette}-${stepNames[nextStepIndex]}` as ColorKey, + alpha: colorToken.alpha, + }; + } else { + return { colorKey: "black", alpha: colorToken.alpha }; + } +} + +export function getPaletteStep( + colorKey: ColorKey, + step: (typeof stepNames)[number], + alpha: number +): ColorToken { + if (colorKey === "black" || colorKey === "white") { + return { colorKey: "black", alpha }; + } + const [palette] = colorKey.split("-"); + return { + colorKey: `${palette}-${step}` as ColorKey, + alpha, + }; +}