ant-design-blazor icon indicating copy to clipboard operation
ant-design-blazor copied to clipboard

Ant Design v5 preparation

Open ldsenow opened this issue 2 years ago • 42 comments

Hi guys,

Are we looking into the upcoming AntD v5 for Blazor?

https://github.com/ant-design/ant-design/issues/33862

It is dropping the less and use css-in-js. It is better for MFE.

ldsenow avatar Nov 09 '22 05:11 ldsenow

Yes, we need to follow up antd 5.0 after the AntDesign Blazor 1.0 release later this year.

But we need to implement css in c# first, do you have any ideas?

ElderJames avatar Nov 09 '22 05:11 ElderJames

I may have some ideas to do the similar thing without css in C#. Let me give it a go and share the repo for you guys to review.

ldsenow avatar Nov 09 '22 06:11 ldsenow

Great, we can implement the FloatButton as a test.

ElderJames avatar Nov 09 '22 07:11 ElderJames

Great, we can implement the FloatButton as a test.

Somehow the website's FloatButton is broken at the moment, so I just created a simple repo (https://github.com/ldsenow/BlazorCssIsolation) with a Button component to demonstrate the potential capabilities.

It is a .net 7 solution but I think it can be run under .net 6.

ldsenow avatar Nov 09 '22 15:11 ldsenow

Look good to me. But it looks like we need a lot of work to synchronize style from design tokens. What do you think?

ElderJames avatar Nov 11 '22 02:11 ElderJames

I will try to see if I can extract from somewhere else and turn that into our css assets somehow. Fingers crossed.

While I'm looking into AntD react's repo, I really love these comments. Designers are respected, haha. image

ldsenow avatar Nov 11 '22 03:11 ldsenow

😂 Maybe we can use some tool for converting ts to c#

https://github.com/mono/TsToCSharp https://github.com/hez2010/TypedocConverter

ElderJames avatar Nov 11 '22 03:11 ElderJames

😂 Maybe we can use some tool for converting ts to c#

https://github.com/mono/TsToCSharp https://github.com/hez2010/TypedocConverter

I guess we can create a project which uses the antd react's npm pkg to export something like e.g. a json file, a css/less file etc., somehow, the blazor end can use it. I will give it a go and see if it works.

ldsenow avatar Nov 11 '22 04:11 ldsenow

Yes, we can use Github Actions to synchronize. Look forward to your good news!

ElderJames avatar Nov 11 '22 04:11 ElderJames

Hi @ldsenow , antd 5.0 have been GA. Do you think we can follow them?

ElderJames avatar Nov 18 '22 07:11 ElderJames

Have been busy last week at work. The react version use React hook context to inject the tokens. I can't find a better way other than using react testing lib to simulate react's environment. BTW, do we need just need the tokens or we also need the values? I guess we want to derive the values based on a primary, right?

ldsenow avatar Nov 18 '22 09:11 ldsenow

I think we need the token and algorithm.

ElderJames avatar Nov 18 '22 10:11 ElderJames

I was looking at the algorithm over the weekend. I can help with that. I guess we dont want to introduce the whole tinycolor right? Re: tokens, i cant find an easy to extract them other than using a converter. I am worried about the maintenance going forward.

ldsenow avatar Nov 21 '22 00:11 ldsenow

Just to share this repo, it inspires me while I'm looking for the solution under the existing blazor Infrastructure.

https://github.com/vanilla-extract-css/vanilla-extract

ldsenow avatar Nov 21 '22 01:11 ldsenow

Yes, we should use converter and shell scripts to synchronize the tokens, instead of manual maintenance.

I have written part of the code of the color palette in feat/color branch before, but some color conversion parts were not implemented, and the test results were wrong. Help is needed for this work.

ElderJames avatar Nov 21 '22 02:11 ElderJames

I am able to extract the base tokens from ts and having a bit of difficulties with getting component tokens. However, i think it is good enough to start applying the algorithms. I have updated the repo with the token extraction, please have a look if this helps. https://github.com/ldsenow/BlazorCssIsolation

ldsenow avatar Nov 22 '22 22:11 ldsenow

This work has made great progress, it helps a lot! Look forward to it being applied to components!

ElderJames avatar Nov 23 '22 05:11 ElderJames

Just to give an update. I spent not much over the weekend due to the World Cup, but i did have some progress on this and hope to ship something out for early review before i may go down to a wrong path.

ldsenow avatar Nov 28 '22 05:11 ldsenow

Great, and I'm looking forward to seeing the implementation soon, so we can move to antd5 and have a lot of the topic issues resolved.

ElderJames avatar Nov 28 '22 05:11 ElderJames

@ldsenow I'm excited to see a lot of updates in your repo.

ElderJames avatar Dec 08 '22 03:12 ElderJames

Sorry for being slow on this. It could have been a bigger progress however antd react changed some props to internal. So i don't have access on those any more and finding a work around. I have mostly ported their algorithms (certainly refactor is required at the end). I will squeeze some time this weekend if I don't get distracted by the world cup.

ldsenow avatar Dec 08 '22 07:12 ldsenow

Hello @ldsenow , have you made any progress recently?

ElderJames avatar Dec 31 '22 04:12 ElderJames

Hello @ldsenow , have you made any progress recently?

Hello, happy new year! I was in holiday and wasn't allowed to have screen time during the break. The bad news is I didnt do anything but the kind of good news is i will continue shortly.

ldsenow avatar Jan 09 '23 03:01 ldsenow

Happy new year, wish you a happy holiday!

ElderJames avatar Jan 09 '23 04:01 ElderJames

Any news?

Postlagerkarte avatar Feb 23 '23 16:02 Postlagerkarte

Any news?

More or less I have the default theme algorithm is ready, dark and compact ones are on the way. Theme variables are generated by the algorithm. As you see in the below screenshot. image

Nested config provider is supported. So the component can have it own override without effecting other components. image

Component tokens/variables will be difficult to extract from Antd React. I need to implement one by one manually based on their logic. Due to this is just for POC purpose, I am confident enough that will 90% work.

@ElderJames If you have time please take a look if the approach is going to be workable / maintainable?

P.S: The link to the POC https://github.com/ldsenow/BlazorCssIsolation

ldsenow avatar Mar 01 '23 01:03 ldsenow

I do not want to rush anyone but what is the ETA for v5? @ElderJames

Postlagerkarte avatar Apr 12 '23 20:04 Postlagerkarte

Any news?

eduardocp avatar May 12 '23 18:05 eduardocp

hello there, one of our contributiors was working on an other project to achieve the v5 styles. I think we can compare this two implementations. https://github.com/ant-design-blazor/cssincs

ElderJames avatar May 17 '23 10:05 ElderJames

I'm working on porting the Antd React style to Blazor. Antd React uses the cssinjs style solution, so I provide the cssincs solution here. The following is an example of style conversion using cssincs. Antd React Style: Alert Component Style

import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';

export interface ComponentToken {}

type AlertToken = FullToken<'Alert'> & {
  alertIconSizeLG: number;
  alertPaddingHorizontal: number;
};

const genAlertTypeStyle = (
  bgColor: string,
  borderColor: string,
  iconColor: string,
  token: AlertToken,
  alertCls: string,
): CSSObject => ({
  backgroundColor: bgColor,
  border: `${token.lineWidth}px ${token.lineType} ${borderColor}`,
  [`${alertCls}-icon`]: {
    color: iconColor,
  },
});

export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
  const {
    componentCls,
    motionDurationSlow: duration,
    marginXS,
    marginSM,
    fontSize,
    fontSizeLG,
    lineHeight,
    borderRadiusLG: borderRadius,
    motionEaseInOutCirc,
    alertIconSizeLG,
    colorText,
    paddingContentVerticalSM,
    alertPaddingHorizontal,
    paddingMD,
    paddingContentHorizontalLG,
  } = token;

  return {
    [componentCls]: {
      ...resetComponent(token),
      position: 'relative',
      display: 'flex',
      alignItems: 'center',
      padding: `${paddingContentVerticalSM}px ${alertPaddingHorizontal}px`, // Fixed horizontal padding here.
      wordWrap: 'break-word',
      borderRadius,

      [`&${componentCls}-rtl`]: {
        direction: 'rtl',
      },

      [`${componentCls}-content`]: {
        flex: 1,
        minWidth: 0,
      },

      [`${componentCls}-icon`]: {
        marginInlineEnd: marginXS,
        lineHeight: 0,
      },

      [`&-description`]: {
        display: 'none',
        fontSize,
        lineHeight,
      },

      '&-message': {
        color: colorText,
      },

      [`&${componentCls}-motion-leave`]: {
        overflow: 'hidden',
        opacity: 1,
        transition: `max-height ${duration} ${motionEaseInOutCirc}, opacity ${duration} ${motionEaseInOutCirc},
        padding-top ${duration} ${motionEaseInOutCirc}, padding-bottom ${duration} ${motionEaseInOutCirc},
        margin-bottom ${duration} ${motionEaseInOutCirc}`,
      },

      [`&${componentCls}-motion-leave-active`]: {
        maxHeight: 0,
        marginBottom: '0 !important',
        paddingTop: 0,
        paddingBottom: 0,
        opacity: 0,
      },
    },

    [`${componentCls}-with-description`]: {
      alignItems: 'flex-start',
      paddingInline: paddingContentHorizontalLG,
      paddingBlock: paddingMD,

      [`${componentCls}-icon`]: {
        marginInlineEnd: marginSM,
        fontSize: alertIconSizeLG,
        lineHeight: 0,
      },

      [`${componentCls}-message`]: {
        display: 'block',
        marginBottom: marginXS,
        color: colorText,
        fontSize: fontSizeLG,
      },

      [`${componentCls}-description`]: {
        display: 'block',
      },
    },

    [`${componentCls}-banner`]: {
      marginBottom: 0,
      border: '0 !important',
      borderRadius: 0,
    },
  };
};

export const genTypeStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
  const {
    componentCls,

    colorSuccess,
    colorSuccessBorder,
    colorSuccessBg,

    colorWarning,
    colorWarningBorder,
    colorWarningBg,

    colorError,
    colorErrorBorder,
    colorErrorBg,

    colorInfo,
    colorInfoBorder,
    colorInfoBg,
  } = token;

  return {
    [componentCls]: {
      '&-success': genAlertTypeStyle(
        colorSuccessBg,
        colorSuccessBorder,
        colorSuccess,
        token,
        componentCls,
      ),
      '&-info': genAlertTypeStyle(colorInfoBg, colorInfoBorder, colorInfo, token, componentCls),
      '&-warning': genAlertTypeStyle(
        colorWarningBg,
        colorWarningBorder,
        colorWarning,
        token,
        componentCls,
      ),
      '&-error': {
        ...genAlertTypeStyle(colorErrorBg, colorErrorBorder, colorError, token, componentCls),
        [`${componentCls}-description > pre`]: {
          margin: 0,
          padding: 0,
        },
      },
    },
  };
};

export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
  const {
    componentCls,
    iconCls,
    motionDurationMid,
    marginXS,
    fontSizeIcon,
    colorIcon,
    colorIconHover,
  } = token;

  return {
    [componentCls]: {
      [`&-action`]: {
        marginInlineStart: marginXS,
      },

      [`${componentCls}-close-icon`]: {
        marginInlineStart: marginXS,
        padding: 0,
        overflow: 'hidden',
        fontSize: fontSizeIcon,
        lineHeight: `${fontSizeIcon}px`,
        backgroundColor: 'transparent',
        border: 'none',
        outline: 'none',
        cursor: 'pointer',

        [`${iconCls}-close`]: {
          color: colorIcon,
          transition: `color ${motionDurationMid}`,
          '&:hover': {
            color: colorIconHover,
          },
        },
      },

      '&-close-text': {
        color: colorIcon,
        transition: `color ${motionDurationMid}`,
        '&:hover': {
          color: colorIconHover,
        },
      },
    },
  };
};

export const genAlertStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSInterpolation => [
  genBaseStyle(token),
  genTypeStyle(token),
  genActionStyle(token),
];

export default genComponentStyleHook('Alert', (token) => {
  const { fontSizeHeading3 } = token;

  const alertToken = mergeToken<AlertToken>(token, {
    alertIconSizeLG: fontSizeHeading3,
    alertPaddingHorizontal: 12, // Fixed value here.
  });

  return [genAlertStyle(alertToken)];
});

Code of using the cssincs, here is blazor alert style code.

using CssInCs;
using static AntDesign.GlobalStyle;

namespace AntDesign
{
    public class AlertToken : TokenWithCommonCls
    {

        public int AlertIconSizeLG { get; set; }
        public int AlertPaddingHorizontal { get; set; }
    }

    public partial class Alert
    {
        public CSSObject GenAlertTypeStyle(
            string bgColor,
            string borderColor,
            string iconColor,
            AlertToken token,
            string alertCls) => new CSSObject()
            {
                BackgroundColor = bgColor,
                Border = $"{token.LineWidth}px {token.LineType} {borderColor}",
                ["${alertCls}-icon"] = new CSSObject()
                {
                    Color = iconColor,
                },
            };

        public CSSObject GenBaseStyle(AlertToken token)
        {
            var (
                componentCls,
                duration,
                marginXS,
                marginSM,
                fontSize,
                fontSizeLG,
                lineHeight,
                borderRadius,
                motionEaseInOutCirc,
                alertIconSizeLG,
                colorText,
                paddingContentVerticalSM,
                alertPaddingHorizontal,
                paddingMD,
                paddingContentHorizontalLG
            ) = (
                token.ComponentCls,
                token.MotionDurationSlow,
                token.MarginXS,
                token.MarginSM,
                token.FontSize,
                token.FontSizeLG,
                token.LineHeight,
                token.BorderRadiusLG,
                token.MotionEaseInOutCirc,
                token.AlertIconSizeLG,
                token.ColorText,
                token.PaddingContentVerticalSM,
                token.AlertPaddingHorizontal,
                token.PaddingMD,
                token.PaddingContentHorizontalLG
            );
            return new CSSObject
            {
                [componentCls] = new CSSObject()
                {
                    ["..."] = ResetComponent(token),
                    Position = "relative",
                    Display = "flex",
                    AlignItems = "center",
                    Padding = $"{paddingContentVerticalSM}px {alertPaddingHorizontal}px", // Fixed horizontal padding here.
                    WordWrap = "break-word",
                    BorderRadius = borderRadius,

                    [$"&{componentCls}-rtl"] = new CSSObject()
                    {
                        Direction = "rtl",
                    },

                    [$"{componentCls}-content"] = new CSSObject()
                    {
                        Flex = 1,
                        MinWidth = 0,
                    },

                    [$"{componentCls}-icon"] = new CSSObject()
                    {
                        MarginInlineEnd = marginXS,
                        LineHeight = 0,
                    },

                    ["&-description"] = new CSSObject()
                    {
                        Display = "none",
                        FontSize = fontSize,
                        LineHeight = lineHeight,
                    },

                    ["&-message"] = new CSSObject()
                    {
                        Color = colorText,
                    },

                    [$"&{componentCls}-motion-leave"] = new CSSObject()
                    {
                        Overflow = "hidden",
                        Opacity = 1,
                        Transition = @$"max-height {duration} {motionEaseInOutCirc}, opacity {duration} {motionEaseInOutCirc},
                            Padding-top {duration} {motionEaseInOutCirc}, padding-bottom {duration} {motionEaseInOutCirc},
                            Margin-bottom {duration} {motionEaseInOutCirc}",
                    },

                    [$"&{componentCls}-motion-leave-active"] = new CSSObject()
                    {
                        MaxHeight = 0,
                        MarginBottom = "0 !important",
                        PaddingTop = 0,
                        PaddingBottom = 0,
                        Opacity = 0,
                    },
                },

                [$"{componentCls}-with-description"] = new CSSObject()
                {
                    AlignItems = "flex-start",
                    PaddingInline = paddingContentHorizontalLG,
                    PaddingBlock = paddingMD,

                    [$"{componentCls}-icon"] = new CSSObject()
                    {
                        MarginInlineEnd = marginSM,
                        FontSize = alertIconSizeLG,
                        LineHeight = 0,
                    },

                    [$"{componentCls}-message"] = new CSSObject()
                    {
                        Display = "block",
                        MarginBottom = marginXS,
                        Color = colorText,
                        FontSize = fontSizeLG,
                    },

                    [$"{componentCls}-description"] = new CSSObject()
                    {
                        Display = "block",
                    },
                },

                [$"{componentCls}-banner"] = new CSSObject()
                {
                    MarginBottom = 0,
                    Border = "0 !important",
                    BorderRadius = 0,
                },
            };
        }
        public CSSObject GenTypeStyle(AlertToken token)
        {
            var (
                componentCls,
                colorSuccess,
                colorSuccessBorder,
                colorSuccessBg,
                colorWarning,
                colorWarningBorder,
                colorWarningBg,
                colorError,
                colorErrorBorder,
                colorErrorBg,
                colorInfo,
                colorInfoBorder,
                colorInfoBg
            ) = (
                token.ComponentCls,
                token.ColorSuccess,
                token.ColorSuccessBorder,
                token.ColorSuccessBg,
                token.ColorWarning,
                token.ColorWarningBorder,
                token.ColorWarningBg,
                token.ColorError,
                token.ColorErrorBorder,
                token.ColorErrorBg,
                token.ColorInfo,
                token.ColorInfoBorder,
                token.ColorInfoBg
            );
            return new CSSObject
            {
                [componentCls] = new CSSObject()
                {
                    ["&-success"] = GenAlertTypeStyle(
                colorSuccessBg,
                colorSuccessBorder,
                colorSuccess,
                token,
                componentCls
              ),
                    ["&-info"] = GenAlertTypeStyle(colorInfoBg, colorInfoBorder, colorInfo, token, componentCls),
                    ["&-warning"] = GenAlertTypeStyle(
                colorWarningBg,
                colorWarningBorder,
                colorWarning,
                token,
                componentCls
              ),
                    ["&-error"] = new CSSObject()
                    {
                        ["..."] = GenAlertTypeStyle(colorErrorBg, colorErrorBorder, colorError, token, componentCls),
                        [$"{componentCls}-description > pre"] = new CSSObject()
                        {
                            Margin = 0,
                            Padding = 0,
                        },
                    },
                },
            };
        }
        public CSSObject GenActionStyle(AlertToken token)
        {
            var (
                componentCls,
                iconCls,
                motionDurationMid,
                marginXS,
                fontSizeIcon,
                colorIcon,
                colorIconHover
            ) = (
                token.ComponentCls,
                token.IconCls,
                token.MotionDurationMid,
                token.MarginXS,
                token.FontSizeIcon,
                token.ColorIcon,
                token.ColorIconHover
            );
            return new CSSObject
            {
                [componentCls] = new CSSObject()
                {
                    ["&-action"] = new CSSObject()
                    {
                        MarginInlineStart = marginXS,
                    },

                    [$"{componentCls}-close-icon"] = new CSSObject()
                    {
                        MarginInlineStart = marginXS,
                        Padding = 0,
                        Overflow = "hidden",
                        FontSize = fontSizeIcon,
                        LineHeight = $"{fontSizeIcon}px",
                        BackgroundColor = "transparent",
                        Border = "none",
                        Outline = "none",
                        Cursor = "pointer",

                        [$"{iconCls}-close"] = new CSSObject()
                        {
                            Color = colorIcon,
                            Transition = $"color {motionDurationMid}",
                            ["&:hover"] = new CSSObject()
                            {
                                Color = colorIconHover,
                            },
                        },
                    },

                    ["&-close-text"] = new CSSObject()
                    {
                        Color = colorIcon,
                        Transition = $"color {motionDurationMid}",
                        ["&:hover"] = new CSSObject()
                        {
                            Color = colorIconHover,
                        },
                    },
                },
            };
        }

        public CSSObject[] GenAlertStyle(AlertToken token) => new CSSObject[]{
          GenBaseStyle(token),
          GenTypeStyle(token),
          GenActionStyle(token),
        };

        protected override CSSObject[] UseStyle(GlobalToken token)
        {
            var fontSizeHeading3 = token.FontSizeHeading3;
            var alertToken = MergeToken<AlertToken>(token, new AlertToken
            {
                AlertIconSizeLG = fontSizeHeading3,
                AlertPaddingHorizontal = 12,
            });

            return GenAlertStyle(alertToken);
        }
    }
}

Yep, you can see that the codes of both are consistent. But one of the problems we face is that we can't automatically convert TS to C# using scripts. We can only manually convert the ts code to csharp code. Since there are so many union types in TS, it cannot be converted directly.

yoli799480165 avatar Jun 01 '23 01:06 yoli799480165