Przeglądaj źródła

Feature: headerSpace system added.

lfabl 1 miesiąc temu
rodzic
commit
a720013b06

+ 10 - 4
example/src/navigation/index.tsx

@@ -7,9 +7,9 @@ import {
 import type RootStackParamList from "./type";
 import {
     NCoreUIKitEmbeddedMenu,
+    NCoreUIKitLocalize,
     NCoreUIKitTheme,
-    MainHeader,
-    NCoreUIKitLocalize
+    MainHeader
 } from "ncore-ui-kit";
 import {
     NavigationContainer,
@@ -32,6 +32,9 @@ const ComponentsStack = createNativeStackNavigator();
 const ComponentsNav = () => {
     return <ComponentsStack.Navigator
         initialRouteName="TextPage"
+        screenOptions={{
+            headerShown: true
+        }}
     >
         <ComponentsStack.Screen
             name="TextPage"
@@ -125,6 +128,9 @@ const RootNav = () => {
             isWorkWithAction: true,
             isWorkWithFlex1: true
         }}
+        customBackgroundColor="moon"
+        isWorkWithSticky={true}
+        glassEffect={5}
     >
         <NCoreUIKitEmbeddedMenu.Render
             navigation={navigation as unknown as NCoreUIKit.Navigation}
@@ -137,12 +143,12 @@ const RootNav = () => {
                 }}
             >
                 <RootStack.Screen
-                    name="Home"
                     component={Home}
+                    name="Home"
                 />
                 <RootStack.Screen
-                    name="Components"
                     component={ComponentsNav}
+                    name="Components"
                 />
             </RootStack.Navigator>
         </NCoreUIKitEmbeddedMenu.Render>

+ 2 - 0
example/src/pages/text/index.tsx

@@ -44,6 +44,7 @@ const TextPage = () => {
 
     useLayoutEffect(() => {
         navigation.setOptions({
+            headerShown: true,
             header: () => <Header
                 isWrapSafeareaContext={false}
                 title={localize("text")}
@@ -55,6 +56,7 @@ const TextPage = () => {
 
     return <PageContainer
         isScrollable={false}
+        isWorkWithHeaderSpace={false}
         style={[
             stylesheet.container
         ]}

+ 9 - 0
src/components/embeddedMenu/index.tsx

@@ -44,6 +44,7 @@ import Button from "../button";
 
 const Menu: RefForwardingComponent<IEmbeddedMenuRef, IEmbeddedMenuProps> = ({
     isWorkWithSafeAreaView = true,
+    isWorkWithHeaderSpace = true,
     isWorkWithAnimation = true,
     renderHeader: RenderHeader,
     renderFooter: RenderFooter,
@@ -52,6 +53,7 @@ const Menu: RefForwardingComponent<IEmbeddedMenuRef, IEmbeddedMenuProps> = ({
     maxWidth = 300,
     minWidth = 75,
     siteLogoProps,
+    headerSpace,
     customTheme,
     navigation,
     isCollapse,
@@ -65,6 +67,7 @@ const Menu: RefForwardingComponent<IEmbeddedMenuRef, IEmbeddedMenuProps> = ({
     ...props
 }, ref) => {
     const {
+        configs,
         colors,
         spaces
     } = NCoreUIKitTheme.useContext(customTheme);
@@ -381,6 +384,12 @@ const Menu: RefForwardingComponent<IEmbeddedMenuRef, IEmbeddedMenuProps> = ({
     const renderSafeAreaContext = () => {
         if(!isWorkWithSafeAreaView) {
             return <Fragment>
+                {isWorkWithHeaderSpace && (configs.headerSpace || headerSpace) ? <View
+                    style={{
+                        height: headerSpace ? headerSpace : configs.headerSpace,
+                        width: "100%"
+                    }}
+                /> : null}
                 {renderHeader()}
                 <View
                     style={[

+ 2 - 0
src/components/embeddedMenu/type.ts

@@ -54,6 +54,7 @@ interface IEmbeddedMenuProps {
     buttons: Array<EmbeddedMenuButton>;
     navigation: NCoreUIKit.Navigation;
     isWorkWithSafeAreaView?: boolean;
+    isWorkWithHeaderSpace?: boolean;
     isAutoClosedOnClosed?: boolean;
     siteLogoProps?: ISiteLogoProps;
     isWorkWithAnimation?: boolean;
@@ -73,6 +74,7 @@ interface IEmbeddedMenuProps {
     isCollapsible?: boolean;
     isAutoClosed?: boolean;
     isCollapse?: boolean;
+    headerSpace?: number;
     onOpened?: (props: {
         id?: string
     }) => void;

+ 7 - 1
src/components/header/index.tsx

@@ -28,6 +28,7 @@ import Text from "../text";
 const Header = <T extends NavigationType>({
     backgroundColor = "default" as keyof NCoreUIKit.ContainerContentColors,
     isWrapSafeareaContext = true,
+    isWorkWithHeaderSpace = true,
     renderTitle: renderTitleProp,
     renderRight: renderRightProp,
     safeAreaViewBackgroundColor,
@@ -35,6 +36,7 @@ const Header = <T extends NavigationType>({
     safeAreaViewStyle,
     isGoBackEnable,
     customTheme,
+    headerSpace,
     titleProps,
     navigation,
     children,
@@ -44,6 +46,7 @@ const Header = <T extends NavigationType>({
     ...props
 }: IHeaderProps<T>) => {
     const {
+        configs,
         colors,
         spaces
     } = NCoreUIKitTheme.useContext(customTheme);
@@ -77,7 +80,10 @@ const Header = <T extends NavigationType>({
         content: contentDynamicStyle
     } = useStyles({
         safeAreaViewBackgroundColor,
+        isWorkWithHeaderSpace,
         backgroundColor,
+        headerSpace,
+        configs,
         spaces,
         colors
     });
@@ -269,7 +275,7 @@ const Header = <T extends NavigationType>({
     };
 
     const renderSafeareaContext = () => {
-        return isWrapSafeareaContext ? renderWithSafeareaView() : <Fragment>
+        return isWrapSafeareaContext && !isWorkWithHeaderSpace ? renderWithSafeareaView() : <Fragment>
             {children ? children : renderContent()}
         </Fragment>;
     };

+ 4 - 0
src/components/header/stylesheet.ts

@@ -63,7 +63,10 @@ const stylesheet = StyleSheet.create({
 
 export const useStyles = ({
     safeAreaViewBackgroundColor,
+    isWorkWithHeaderSpace,
     backgroundColor,
+    headerSpace,
+    configs,
     spaces,
     colors
 }: HeaderDynamicStyleType) => {
@@ -72,6 +75,7 @@ export const useStyles = ({
             backgroundColor: safeAreaViewBackgroundColor ? colors.content.container[safeAreaViewBackgroundColor] : colors.content.container[backgroundColor]
         } as Mutable<ViewStyle>,
         content: {
+            marginTop: isWorkWithHeaderSpace ? headerSpace ? headerSpace : configs.headerSpace : 0,
             backgroundColor: colors.content.container[backgroundColor],
             padding: spaces.spacingMd
         } as Mutable<ViewStyle>,

+ 5 - 0
src/components/header/type.ts

@@ -11,8 +11,11 @@ import type ITextProps from "../text/type";
 export type HeaderDynamicStyleType = {
     safeAreaViewBackgroundColor?: keyof NCoreUIKit.ContainerContentColors;
     backgroundColor: keyof NCoreUIKit.ContainerContentColors;
+    configs: NCoreUIKit.ActivePalette["configs"];
     spaces: NCoreUIKit.ActivePalette["spaces"];
     colors: NCoreUIKit.ActivePalette["colors"];
+    isWorkWithHeaderSpace?: boolean;
+    headerSpace?: number;
 };
 
 export type NavigationType = unknown;
@@ -30,6 +33,7 @@ interface IHeaderProps<T> {
     };
     safeAreaViewStyle?: StyleProp<ViewStyle>;
     isWrapSafeareaContext?: boolean;
+    isWorkWithHeaderSpace?: boolean;
     style?: StyleProp<ViewStyle>;
     actions?: Array<ActionItem>;
     isGoBackEnable?: boolean;
@@ -43,6 +47,7 @@ interface IHeaderProps<T> {
     renderLeft?: (props: {
         navigation?: T
     }) => ReactNode;
+    headerSpace?: number;
     children?: ReactNode;
     navigation?: T;
     title?: string;

+ 57 - 4
src/components/mainHeader/index.tsx

@@ -1,3 +1,7 @@
+import {
+    useEffect,
+    useState
+} from "react";
 import {
     View
 } from "react-native";
@@ -11,21 +15,29 @@ import {
 import {
     SafeAreaView
 } from "react-native-safe-area-context";
-import SiteLogo from "../siteLogo";
 import {
     Portal
 } from "../../helpers/portalize";
+import SiteLogo from "../siteLogo";
 
 const MainHeader = ({
     isWorkWithSafeAreaView = true,
+    isWorkWithStickySpace = false,
+    isSetAutoHeaderSpace = true,
     isWorkWithSeperator = false,
     backgroundColor = "default",
     portalName = "main-header",
     isWorkWithPortal = false,
+    isWorkWithSticky = false,
     renderRight: RenderRight,
     renderLeft: RenderLeft,
+    customBackgroundColor,
+    onContentHeight,
     siteLogoProps,
-    children
+    glassEffect,
+    children,
+    style,
+    ...props
 }: IMainHeader) => {
     const {
         borders,
@@ -34,15 +46,28 @@ const MainHeader = ({
     } = NCoreUIKitTheme.useContext();
 
     const {
+        safeAreaView: safeAreaViewDynamicStyle,
         container: containerDynamicStyle
     } = useStyles({
+        isWorkWithSafeAreaView,
         isWorkWithSeperator,
+        isWorkWithSticky,
         backgroundColor,
+        glassEffect,
         borders,
         spaces,
         colors
     });
 
+    const [
+        contentHeight,
+        setContentHeight
+    ] = useState<null | number>(null);
+
+    useEffect(() => {
+        if(onContentHeight) onContentHeight(contentHeight);
+    }, [contentHeight]);
+
     const renderRight = () => {
         if(RenderRight) {
             return <RenderRight/>;
@@ -65,10 +90,29 @@ const MainHeader = ({
 
     const renderContent = () => {
         return <View
+            {...props}
             style={[
+                style,
                 stylesheet.container,
                 containerDynamicStyle
             ]}
+            onLayout={(event) => {
+                if(props && props.onLayout) {
+                    props.onLayout(event);
+                }
+
+                if(isWorkWithSticky) {
+                    const _contentHeight = event.nativeEvent.layout.height;
+
+                    setContentHeight(_contentHeight);
+
+                    if(isSetAutoHeaderSpace) {
+                        NCoreUIKitTheme.updateConfigs({
+                            headerSpace: _contentHeight
+                        });
+                    }
+                }
+            }}
         >
             {renderLeft()}
             {renderRight()}
@@ -92,6 +136,9 @@ const MainHeader = ({
             edges={[
                 "top"
             ]}
+            style={[
+                safeAreaViewDynamicStyle
+            ]}
         >
             {renderContentContainer()}
         </SafeAreaView>;
@@ -103,9 +150,15 @@ const MainHeader = ({
                 stylesheet.baseContainer
             ]}
         >
-            {isWorkWithPortal ? children : null}
             {isWorkWithSafeAreaView ? renderSafeAreaView() : renderContentContainer()}
-            {isWorkWithPortal ? null : children}
+            {isWorkWithSticky && isWorkWithStickySpace ? <View
+                style={{
+                    backgroundColor: customBackgroundColor ? colors.project[customBackgroundColor] : colors.content.container[backgroundColor],
+                    height: contentHeight,
+                    width: "100%"
+                }}
+            /> : null}
+            {children}
         </View>;
     };
 

+ 31 - 2
src/components/mainHeader/stylesheet.ts

@@ -8,6 +8,9 @@ import type {
 import type {
     Mutable
 } from "../../types";
+import {
+    webStyle
+} from "../../utils";
 
 const stylesheet = StyleSheet.create({
     container: {
@@ -22,16 +25,26 @@ const stylesheet = StyleSheet.create({
 });
 
 export const useStyles = ({
+    isWorkWithSafeAreaView,
+    customBackgroundColor,
     isWorkWithSeperator,
+    isWorkWithSticky,
     backgroundColor,
+    glassEffect,
     borders,
     spaces,
     colors
 }: MainHeaderDynamicStyle) => {
     const styles = {
         container: {
-            backgroundColor: backgroundColor ? colors.content.container[backgroundColor] : colors.content.container.default,
-            padding: spaces.spacingMd
+            backgroundColor: customBackgroundColor ? colors.project[customBackgroundColor] : backgroundColor ? colors.content.container[backgroundColor] : colors.content.container.default,
+            padding: spaces.spacingMd,
+            ...webStyle({
+                WebkitBackdropFilter: `blur(${glassEffect}px)`,
+                backdropFilter: `blur(${glassEffect}px)`
+            })
+        } as Mutable<ViewStyle>,
+        safeAreaView: {
         } as Mutable<ViewStyle>
     };
 
@@ -40,6 +53,22 @@ export const useStyles = ({
         styles.container.borderBottomWidth = borders.line;
     }
 
+    if(isWorkWithSticky) {
+        if(isWorkWithSafeAreaView) {
+            styles.safeAreaView.position = "absolute";
+            styles.safeAreaView.zIndex = 999;
+            styles.safeAreaView.right = 0;
+            styles.safeAreaView.left = 0;
+            styles.safeAreaView.top = 0;
+        } else {
+            styles.container.position = "absolute";
+            styles.container.zIndex = 999;
+            styles.container.right = 0;
+            styles.container.left = 0;
+            styles.container.top = 0;
+        }
+    }
+
     return styles;
 };
 

+ 14 - 1
src/components/mainHeader/type.ts

@@ -1,24 +1,37 @@
 import type {
     ReactNode
 } from "react";
+import type {
+    ViewProps
+} from "react-native";
 import type ISiteLogoProps from "../siteLogo/type";
 
 export type MainHeaderDynamicStyle = {
+    customBackgroundColor?: keyof NCoreUIKit.ProjectColorPalette;
     backgroundColor?: keyof NCoreUIKit.ContainerContentColors;
     borders: NCoreUIKit.ActivePalette["borders"];
     colors: NCoreUIKit.ActivePalette["colors"];
     spaces: NCoreUIKit.ActivePalette["spaces"];
+    isWorkWithSafeAreaView?: boolean;
     isWorkWithSeperator?: boolean;
+    isWorkWithSticky?: boolean;
+    glassEffect?: number;
 };
 
-interface IMainHeader {
+interface IMainHeader extends ViewProps {
+    customBackgroundColor?: keyof NCoreUIKit.ProjectColorPalette;
     backgroundColor?: keyof NCoreUIKit.ContainerContentColors;
+    onContentHeight?: (contentHeight: number | null) => void;
     isWorkWithSafeAreaView?: boolean;
+    isWorkWithStickySpace?: boolean;
+    isSetAutoHeaderSpace?: boolean;
     siteLogoProps?: ISiteLogoProps;
     isWorkWithSeperator?: boolean;
     renderRight?: () => ReactNode;
     renderLeft?: () => ReactNode;
     isWorkWithPortal?: boolean;
+    isWorkWithSticky?: boolean;
+    glassEffect?: number;
     children?: ReactNode;
     portalName?: string;
 };

+ 6 - 2
src/components/pageContainer/index.tsx

@@ -18,6 +18,7 @@ import {
 const PageContainer: FC<IPageContainerProps> = ({
     backgroundColor = "default" as keyof NCoreUIKit.ContainerContentColors,
     isWrapSafeareaContext = false,
+    isWorkWithHeaderSpace = true,
     safeAreaViewBackgroundColor,
     isCustomPadding = false,
     isScrollable = false,
@@ -31,6 +32,7 @@ const PageContainer: FC<IPageContainerProps> = ({
     ...props
 }) => {
     const {
+        configs,
         colors,
         spaces
     } = NCoreUIKitTheme.useContext(customTheme);
@@ -49,7 +51,8 @@ const PageContainer: FC<IPageContainerProps> = ({
             {...scrollViewProps}
             contentContainerStyle={[
                 isCustomPadding ? null : {
-                    padding: spaces.spacingMd
+                    padding: spaces.spacingMd,
+                    paddingTop: spaces.spacingMd + (isWorkWithHeaderSpace ? configs.headerSpace : 0)
                 },
                 scrollViewProps?.contentContainerStyle
             ]}
@@ -81,7 +84,8 @@ const PageContainer: FC<IPageContainerProps> = ({
                     backgroundColor: colors.content.container[backgroundColor]
                 },
                 isCustomPadding ? null : {
-                    padding: spaces.spacingMd
+                    padding: spaces.spacingMd,
+                    paddingTop: spaces.spacingMd + (isWorkWithHeaderSpace ? configs.headerSpace : 0)
                 },
                 stylesheet.container,
                 style

+ 1 - 0
src/components/pageContainer/type.ts

@@ -15,6 +15,7 @@ interface IPageContainerProps {
     scrollViewProps?: ScrollViewProps;
     renderOverlays?: () => ReactNode;
     isWrapSafeareaContext?: boolean;
+    isWorkWithHeaderSpace?: boolean;
     style?: StyleProp<ViewStyle>;
     isCustomPadding?: boolean;
     isScrollable?: boolean;

+ 3 - 0
src/context/embeddedMenu.tsx

@@ -200,6 +200,7 @@ class NCoreUIKitEmbeddedMenu extends NCoreContext<EmbeddedMenuContextType, Confi
     };
 
     Render = ({
+        headerSpace,
         navigation,
         children,
         index,
@@ -207,6 +208,7 @@ class NCoreUIKitEmbeddedMenu extends NCoreContext<EmbeddedMenuContextType, Confi
     }: {
         navigation: NCoreUIKit.Navigation;
         children?: ReactNode;
+        headerSpace?: number;
         index?: number;
         id?: string;
     }) => {
@@ -223,6 +225,7 @@ class NCoreUIKitEmbeddedMenu extends NCoreContext<EmbeddedMenuContextType, Confi
                 {...currentMenu}
                 key={`NCoreUIKit-Menu-${currentMenu.id}`}
                 id={currentMenu.id as string}
+                headerSpace={headerSpace}
                 close={() => {
                     if(currentMenu.isAutoClosed) {
                         this.close({

+ 23 - 4
src/context/theme.tsx

@@ -6,7 +6,8 @@ import {
     type GapPropagationType,
     type ThemeContextType,
     type SharpnessType,
-    type ThemesType
+    type ThemesType,
+    type ThemeConfigs
 } from "../types";
 import NCoreContext, {
     type ConfigType
@@ -90,7 +91,10 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
             activePalette: initialSelectedPalette ?? "nibgat",
             activeTheme: initialSelectedTheme ?? "dark",
             borders: defaultPaletteData.shapes.borders,
-            colors: initialTheme
+            colors: initialTheme,
+            configs: {
+                headerSpace: 0
+            }
         }, {
             key: "NCoreUIKitTheme"
         });
@@ -107,6 +111,15 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
         this.state = activeState as ThemeContextType;
     }
 
+    updateConfigs = (configs: Partial<ThemeConfigs>) => {
+        this.setState({
+            configs: {
+                ...this.state.configs,
+                ...configs
+            }
+        });
+    };
+
     prepare = (props?: {
         activePalette?: keyof NCoreUIKit.PaletteKey;
         activeGapPropagation?: GapPropagationType;
@@ -133,7 +146,10 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
             activeSharpness: sharpness,
             activePalette: palette,
             colors: defaultTheme,
-            activeTheme: theme
+            activeTheme: theme,
+            configs: {
+                headerSpace: 0
+            }
         };
 
         if(!this.projectThemes || (this.projectThemes && !this.projectThemes.palettes.length)) {
@@ -191,7 +207,10 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
             borders: shapes.borders,
             activePalette: palette,
             activeTheme: theme,
-            colors: colors
+            colors: colors,
+            configs: {
+                headerSpace: 0
+            }
         };
 
         return newState;

+ 4 - 1
src/types/index.ts

@@ -15,6 +15,7 @@ import {
 import {
     type GapPropagationType,
     type SharpnessType,
+    type ThemeConfigs,
     type PaletteType,
     type ThemesType,
     type ThemeType
@@ -34,7 +35,8 @@ export type {
 
 export type {
     ThemeContextStateType,
-    ThemeContextType
+    ThemeContextType,
+    ThemeConfigs
 } from "./theme";
 
 export type {
@@ -91,6 +93,7 @@ declare global {
         interface ActivePalette {
             inlineSpaces: InlineSpaces;
             typography: Typography;
+            configs: ThemeConfigs;
             colors: ThemeTokens;
             radiuses: Radiuses;
             borders: Borders;

+ 5 - 0
src/types/theme.ts

@@ -16,6 +16,10 @@ export type ThemeType = "light" | "dark";
 
 export type ThemeContextStateType = Partial<ThemeContextType>;
 
+export type ThemeConfigs = {
+    headerSpace: number;
+};
+
 export type ThemeContextType = {
     paletteKeys: Array<keyof NCoreUIKit.PaletteKey>;
     themeKeys: Array<keyof NCoreUIKit.ThemeKey>;
@@ -29,4 +33,5 @@ export type ThemeContextType = {
     radiuses: NCoreUIKit.Radiuses;
     borders: NCoreUIKit.Borders;
     spaces: NCoreUIKit.Spaces;
+    configs: ThemeConfigs;
 };