Przeglądaj źródła

Feature: Core system completed.

lfabl 3 miesięcy temu
rodzic
commit
46547c6c68
52 zmienionych plików z 1988 dodań i 119 usunięć
  1. 1 1
      eslint-local-rules/index.js
  2. 3 0
      example/package.json
  3. 15 6
      example/src/index.tsx
  4. 5 3
      package.json
  5. BIN
      src/assets/fonts/Geist-Black.ttf
  6. BIN
      src/assets/fonts/Geist-Bold.ttf
  7. BIN
      src/assets/fonts/Geist-ExtraBold.ttf
  8. BIN
      src/assets/fonts/Geist-ExtraLight.ttf
  9. BIN
      src/assets/fonts/Geist-Light.ttf
  10. BIN
      src/assets/fonts/Geist-Medium.ttf
  11. BIN
      src/assets/fonts/Geist-Regular.ttf
  12. BIN
      src/assets/fonts/Geist-SemiBold.ttf
  13. BIN
      src/assets/fonts/Geist-Thin.ttf
  14. 42 0
      src/assets/svg/badgeAlertIcon/index.tsx
  15. 40 0
      src/assets/svg/badgeDangerIcon/index.tsx
  16. 40 0
      src/assets/svg/badgeInfoIcon/index.tsx
  17. 50 0
      src/assets/svg/badgeQuestionMarkIcon/index.tsx
  18. 40 0
      src/assets/svg/badgeRightIcon/index.tsx
  19. 50 0
      src/assets/svg/badgeSuccessIcon/index.tsx
  20. 40 0
      src/assets/svg/chevronRightIcon/index.tsx
  21. 40 0
      src/assets/svg/cleanIcon/index.tsx
  22. 50 0
      src/assets/svg/eyeClosedIcon/index.tsx
  23. 50 0
      src/assets/svg/eyeOpenedIcon/index.tsx
  24. 39 0
      src/assets/svg/index.ts
  25. 90 0
      src/assets/svg/loadingIcon/index.tsx
  26. 0 0
      src/components/button/index.tsx
  27. 0 0
      src/components/button/stylesheet.ts
  28. 0 0
      src/components/button/type.ts
  29. 36 0
      src/components/index.ts
  30. 28 0
      src/components/loading/index.tsx
  31. 10 0
      src/components/loading/stylesheet.ts
  32. 18 0
      src/components/loading/type.ts
  33. 134 0
      src/components/modal/index.tsx
  34. 28 0
      src/components/modal/stylesheet.ts
  35. 32 0
      src/components/modal/type.ts
  36. 40 0
      src/components/text/index.tsx
  37. 18 0
      src/components/text/type.ts
  38. 55 0
      src/context/index.tsx
  39. 199 0
      src/context/localize.tsx
  40. 106 0
      src/context/modal.tsx
  41. 211 0
      src/context/theme.tsx
  42. 16 0
      src/helpers/index.ts
  43. 76 0
      src/helpers/theme/palette.ts
  44. 106 0
      src/helpers/theme/shapes.ts
  45. 41 0
      src/helpers/theme/typography.ts
  46. 10 11
      src/index.tsx
  47. 6 3
      src/types/icon.ts
  48. 17 14
      src/types/index.ts
  49. 2 2
      src/types/locale.ts
  50. 1 1
      src/types/modal.ts
  51. 61 61
      src/variants/themes/default.json
  52. 142 17
      yarn.lock

+ 1 - 1
eslint-local-rules/index.js

@@ -381,7 +381,7 @@ module.exports = {
                 if (source === "react-native") return 2;
 
                 if (source.startsWith("./")) {
-                    if (code.includes("import type") || source.includes(".types")) {
+                    if (code.includes("import type") || source.includes(".types") || source.includes("/type")) {
                         return 3.1;
                     }
 

+ 3 - 0
example/package.json

@@ -13,9 +13,12 @@
         "@expo/metro-runtime": "~55.0.6",
         "expo": "~55.0.4",
         "expo-status-bar": "~55.0.4",
+        "ncore-context": "^1.0.5",
         "react": "19.2.0",
         "react-dom": "19.2.0",
         "react-native": "0.83.2",
+        "react-native-portalize": "^1.0.7",
+        "react-native-svg": "^15.15.3",
         "react-native-web": "~0.21.0"
     },
     "private": true,

+ 15 - 6
example/src/index.tsx

@@ -1,20 +1,28 @@
 import {
     StyleSheet,
-    Text,
     View
 } from "react-native";
 import {
-    multiply
+    setupNCoreUIKit,
+    Text
 } from "ncore-ui-kit-mobile";
 
-console.log(multiply(3, 3));
-// const result = multiply(3, 7);
+const NCoreUIKitBase = setupNCoreUIKit({
+    initialSelectedGapPropagation: "compact",
+    initialSelectedTheme: "light"
+});
 
-export default function App() {
+const App = () => {
     return <View style={styles.container}>
         <Text>Result: </Text>
     </View>;
-}
+};
+
+const ContextAPI = () => {
+    return <NCoreUIKitBase.Provider>
+        <App/>
+    </NCoreUIKitBase.Provider>;
+};
 
 const styles = StyleSheet.create({
     container: {
@@ -23,3 +31,4 @@ const styles = StyleSheet.create({
         flex: 1
     }
 });
+export default ContextAPI;

+ 5 - 3
package.json

@@ -84,11 +84,12 @@
         "typescript-eslint": "8.46.1"
     },
     "peerDependencies": {
-        "react-native-portalize": ">= 1.0.7",
         "lucide-react-native": ">= 0.577.0",
         "ncore-context": ">= 1.0.5",
         "react": "*",
-        "react-native": "*"
+        "react-native": "*",
+        "react-native-portalize": ">= 1.0.7",
+        "react-native-svg": ">= 15.15.3"
     },
     "workspaces": [
         "example"
@@ -142,6 +143,7 @@
     "dependencies": {
         "lucide-react-native": "0.577.0",
         "ncore-context": "1.0.5",
-        "react-native-portalize": "1.0.7"
+        "react-native-portalize": "1.0.7",
+        "react-native-svg": "15.15.3"
     }
 }

BIN
src/assets/fonts/Geist-Black.ttf


BIN
src/assets/fonts/Geist-Bold.ttf


BIN
src/assets/fonts/Geist-ExtraBold.ttf


BIN
src/assets/fonts/Geist-ExtraLight.ttf


BIN
src/assets/fonts/Geist-Light.ttf


BIN
src/assets/fonts/Geist-Medium.ttf


BIN
src/assets/fonts/Geist-Regular.ttf


BIN
src/assets/fonts/Geist-SemiBold.ttf


BIN
src/assets/fonts/Geist-Thin.ttf


+ 42 - 0
src/assets/svg/badgeAlertIcon/index.tsx

@@ -0,0 +1,42 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgBadgeAlertIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    const finalColor = customColor ? customColor : colors.content.icon[color];
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="M10 6.667V10m0 3.333h.008m-6.8-6.15a3.333 3.333 0 0 1 3.984-3.975 3.333 3.333 0 0 1 5.616 0 3.333 3.333 0 0 1 3.984 3.984 3.333 3.333 0 0 1 0 5.616 3.333 3.333 0 0 1-3.975 3.984 3.333 3.333 0 0 1-5.625 0 3.333 3.333 0 0 1-3.984-3.975 3.335 3.335 0 0 1 0-5.634Z"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+    </Svg>;
+};
+export default SvgBadgeAlertIcon;

+ 40 - 0
src/assets/svg/badgeDangerIcon/index.tsx

@@ -0,0 +1,40 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgBadgeDangerIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="m12.5 7.5-5 5m0-5 5 5M3.208 7.183a3.333 3.333 0 0 1 3.984-3.975 3.333 3.333 0 0 1 5.616 0 3.333 3.333 0 0 1 3.984 3.984 3.333 3.333 0 0 1 0 5.616 3.333 3.333 0 0 1-3.975 3.984 3.333 3.333 0 0 1-5.625 0 3.333 3.333 0 0 1-3.984-3.975 3.335 3.335 0 0 1 0-5.634Z"
+            stroke={customColor ? customColor : colors.content.icon[color]}
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+        />
+    </Svg>;
+};
+export default SvgBadgeDangerIcon;

+ 40 - 0
src/assets/svg/badgeInfoIcon/index.tsx

@@ -0,0 +1,40 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgBadgeInfoIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="M10 13.333V10m0-3.333h.008m-6.8.516a3.333 3.333 0 0 1 3.984-3.975 3.333 3.333 0 0 1 5.616 0 3.333 3.333 0 0 1 3.984 3.984 3.333 3.333 0 0 1 0 5.616 3.333 3.333 0 0 1-3.975 3.984 3.333 3.333 0 0 1-5.625 0 3.333 3.333 0 0 1-3.984-3.975 3.335 3.335 0 0 1 0-5.634Z"
+            stroke={customColor ? customColor : colors.content.icon[color]}
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+        />
+    </Svg>;
+};
+export default SvgBadgeInfoIcon;

+ 50 - 0
src/assets/svg/badgeQuestionMarkIcon/index.tsx

@@ -0,0 +1,50 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgBadgeQuestionMarkIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    const finalColor = customColor ? customColor : colors.content.icon[color];
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="m12.5 7.5-5 5m0-5 5 5M3.208 7.183a3.333 3.333 0 0 1 3.984-3.975 3.333 3.333 0 0 1 5.616 0 3.333 3.333 0 0 1 3.984 3.984 3.333 3.333 0 0 1 0 5.616 3.333 3.333 0 0 1-3.975 3.984 3.333 3.333 0 0 1-5.625 0 3.333 3.333 0 0 1-3.984-3.975 3.335 3.335 0 0 1 0-5.634Z"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+        <Path
+            d="M7.575 7.5a2.5 2.5 0 0 1 4.858.833c0 1.667-2.5 2.5-2.5 2.5M10 14.167h.008"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinecap="round"
+            strokeLinejoin="round"
+            stroke={finalColor}
+            strokeWidth={1.167}
+        />
+    </Svg>;
+};
+export default SvgBadgeQuestionMarkIcon;

+ 40 - 0
src/assets/svg/badgeRightIcon/index.tsx

@@ -0,0 +1,40 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgChevronRight = ({
+    color = "default",
+    customColor,
+    size = 30,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 26 / size;
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            stroke={customColor ? customColor : colors.content.icon[color]}
+            transform={`scale(${1 / pathScale})`}
+            d="m7.5 24 11-11-11-11"
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.55}
+        />
+    </Svg>;
+};
+export default SvgChevronRight;

+ 50 - 0
src/assets/svg/badgeSuccessIcon/index.tsx

@@ -0,0 +1,50 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgBadgeSuccessIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    const finalColor = customColor ? customColor : colors.content.icon[color];
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="m12.5 7.5-5 5m0-5 5 5M3.208 7.183a3.333 3.333 0 0 1 3.984-3.975 3.333 3.333 0 0 1 5.616 0 3.333 3.333 0 0 1 3.984 3.984 3.333 3.333 0 0 1 0 5.616 3.333 3.333 0 0 1-3.975 3.984 3.333 3.333 0 0 1-5.625 0 3.333 3.333 0 0 1-3.984-3.975 3.335 3.335 0 0 1 0-5.634Z"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+        <Path
+            transform={`scale(${1 / pathScale})`}
+            d="m7.5 10 1.667 1.667L12.5 8.333"
+            strokeLinecap="round"
+            strokeLinejoin="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+    </Svg>;
+};
+export default SvgBadgeSuccessIcon;

+ 40 - 0
src/assets/svg/chevronRightIcon/index.tsx

@@ -0,0 +1,40 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgChevronRight = ({
+    color = "default",
+    customColor,
+    size = 30,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 26 / size;
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            stroke={customColor ? customColor : colors.content.icon[color]}
+            transform={`scale(${1 / pathScale})`}
+            d="m7.5 24 11-11-11-11"
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.55}
+        />
+    </Svg>;
+};
+export default SvgChevronRight;

+ 40 - 0
src/assets/svg/cleanIcon/index.tsx

@@ -0,0 +1,40 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgCleanIcon = ({
+    color = "default",
+    customColor,
+    size = 20,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 27 / size;
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="M13.5 25C19.851 25 25 19.851 25 13.5S19.851 2 13.5 2 2 7.149 2 13.5 7.149 25 13.5 25ZM16.95 10.05l-6.9 6.9m0-6.9 6.9 6.9"
+            stroke={customColor ? customColor : colors.content.icon[color]}
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.25}
+        />
+    </Svg>;
+};
+export default SvgCleanIcon;

+ 50 - 0
src/assets/svg/eyeClosedIcon/index.tsx

@@ -0,0 +1,50 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgEyeClosed = ({
+    color = "default",
+    customColor,
+    size = 30,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    const finalColor = customColor ? customColor : colors.content.icon[color];
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="M8.958 4.233a8.954 8.954 0 0 1 9.338 5.48c.069.187.069.392 0 .58a9.002 9.002 0 0 1-1.204 2.075m-5.341-.566a2.5 2.5 0 0 1-3.536-3.535"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+        <Path
+            d="M14.58 14.586a8.957 8.957 0 0 1-12.848-4.293.833.833 0 0 1 0-.58 8.958 8.958 0 0 1 3.705-4.285M1.68 1.67l16.667 16.667"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            strokeWidth={1.167}
+            stroke={finalColor}
+        />
+    </Svg>;
+};
+export default SvgEyeClosed;

+ 50 - 0
src/assets/svg/eyeOpenedIcon/index.tsx

@@ -0,0 +1,50 @@
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const SvgEyeOpened = ({
+    color = "default",
+    customColor,
+    size = 30,
+    ...props
+}: INCoreUIKitIconProps) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const pathScale = 20 / size;
+
+    const finalColor = customColor ? customColor : colors.content.icon[color];
+
+    return <Svg
+        height={size}
+        width={size}
+        fill="none"
+        {...props}
+    >
+        <Path
+            d="M1.732 10.293a.833.833 0 0 1 0-.58 8.958 8.958 0 0 1 16.563 0c.07.187.07.393 0 .58a8.958 8.958 0 0 1-16.563 0Z"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            stroke={finalColor}
+            strokeWidth={1.167}
+        />
+        <Path
+            d="M10.014 12.503a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z"
+            transform={`scale(${1 / pathScale})`}
+            strokeLinejoin="round"
+            strokeLinecap="round"
+            stroke={finalColor}
+            strokeWidth={1.167}
+        />
+    </Svg>;
+};
+export default SvgEyeOpened;

+ 39 - 0
src/assets/svg/index.ts

@@ -0,0 +1,39 @@
+export {
+    default as LoadingIcon
+} from "./loadingIcon";
+
+export {
+    default as CleanIcon
+} from "./cleanIcon";
+
+export {
+    default as ChevronRightIcon
+} from "./chevronRightIcon";
+
+export {
+    default as EyeClosedIcon
+} from "./eyeClosedIcon";
+
+export {
+    default as EyeOpenedIcon
+} from "./eyeOpenedIcon";
+
+export {
+    default as BadgeInfoIcon
+} from "./badgeInfoIcon";
+
+export {
+    default as BadgeDangerIcon
+} from "./badgeDangerIcon";
+
+export {
+    default as BadgeSuccessIcon
+} from "./badgeSuccessIcon";
+
+export {
+    default as BadgeQuestionMarkIcon
+} from "./badgeQuestionMarkIcon";
+
+export {
+    default as BadgeAlertIcon
+} from "./badgeAlertIcon";

+ 90 - 0
src/assets/svg/loadingIcon/index.tsx

@@ -0,0 +1,90 @@
+import {
+    useEffect,
+    useRef
+} from "react";
+import {
+    Animated,
+    Easing
+} from "react-native";
+import {
+    NCoreUIKitTheme
+} from "../../..";
+import {
+    type INCoreUIKitIconProps
+} from "../../../types";
+import {
+    Path,
+    Svg
+} from "react-native-svg";
+
+const AnimatedSvg = Animated.createAnimatedComponent(Svg);
+
+const SvgLoadingIcon = ({
+    color = "onPrimary",
+    customColor,
+    size = 22,
+    ...props
+}: INCoreUIKitIconProps & {
+    className?: string;
+}) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const animValue = useRef(new Animated.Value(0)).current;
+
+    const pathScale = 38 / size;
+
+    useEffect(() => {
+        Animated.loop(
+            Animated.timing(animValue, {
+                toValue: 1,
+                duration: 2000,
+                easing: Easing.linear,
+                useNativeDriver: true
+            })
+        ).start();
+    }, [animValue]);
+
+    const rotate = animValue.interpolate({
+        inputRange: [
+            0,
+            1
+        ],
+        outputRange: [
+            "0deg",
+            "360deg"
+        ]
+    });
+
+    return (
+        <span
+            style={{
+                height: size,
+                width: size
+            }}
+        >
+            <AnimatedSvg
+                height={size}
+                width={size}
+                fill="none"
+                style={{
+                    ...props.style,
+                    ...rotate
+                }}
+                {...props}
+            >
+                <Path
+                    d="M11.885 7.303 19 3.194l7.115 4.109v8.215l7.095 4.09v8.215l-7.115 4.108-7.076-4.108-7.114 4.108-7.115-4.108v-8.216l7.095-4.089V7.303Z"
+                    stroke={customColor ? customColor : colors.content.icon[color]}
+                    transform={`scale(${1 / pathScale})`}
+                    strokeLinejoin="round"
+                    strokeLinecap="round"
+                    clipRule="evenodd"
+                    strokeWidth={3}
+                />
+            </AnimatedSvg>
+        </span>
+    );
+};
+export default SvgLoadingIcon;

+ 0 - 0
src/components/button/index.tsx


+ 0 - 0
src/components/button/stylesheet.ts


+ 0 - 0
src/components/button/type.ts


+ 36 - 0
src/components/index.ts

@@ -0,0 +1,36 @@
+export {
+    default as Text
+} from "./text";
+/*
+export {
+    default as Button
+} from "./button";
+
+export {
+    default as Dialog
+} from "./dialog";
+
+export type {
+    IDialogRef
+} from "./dialog/type";
+*/
+export {
+    default as Modal
+} from "./modal";
+
+export type {
+    IModalRef
+} from "./modal/type";
+
+export {
+    default as Loading
+} from "./loading";
+/*
+export {
+    default as TextInput
+} from "./textInput";
+
+export {
+    default as SelectBox
+} from "./selectBox";
+*/

+ 28 - 0
src/components/loading/index.tsx

@@ -0,0 +1,28 @@
+import {
+    type FC
+} from "react";
+import type ILoadingProps from "./type";
+import stylesheet from "./stylesheet";
+import {
+    LoadingIcon
+} from "../../assets/svg";
+
+const Loading: FC<ILoadingProps> = ({
+    color = "primary" as keyof NCoreUIKit.IconContentColors,
+    customColor,
+    size = 22,
+    style,
+    ...props
+}) => {
+    return <LoadingIcon
+        customColor={customColor}
+        color={color}
+        size={size}
+        style={[
+            stylesheet.container,
+            style
+        ]}
+        {...props}
+    />;
+};
+export default Loading;

+ 10 - 0
src/components/loading/stylesheet.ts

@@ -0,0 +1,10 @@
+import {
+    StyleSheet
+} from "react-native";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        transformOrigin: "center"
+    }
+});
+export default stylesheet;

+ 18 - 0
src/components/loading/type.ts

@@ -0,0 +1,18 @@
+import {
+    type ReactNode
+} from "react";
+import {
+    type SvgProps
+} from "react-native-svg";
+
+interface ILoadingProps {
+    color?: keyof NCoreUIKit.IconContentColors;
+    style?: SvgProps["style"];
+    customColor?: string;
+    children?: ReactNode;
+    size?: number;
+    id?: string;
+}
+export type {
+    ILoadingProps as default
+};

+ 134 - 0
src/components/modal/index.tsx

@@ -0,0 +1,134 @@
+import {
+    useImperativeHandle,
+    forwardRef,
+    useEffect,
+    useRef
+} from "react";
+import {
+    Animated,
+    Easing,
+    View
+} from "react-native";
+import type IModalProps from "./type";
+import {
+    type IModalRef
+} from "./type";
+import stylesheet from "./stylesheet";
+import {
+    NCoreUIKitTheme
+} from "../..";
+import {
+    type RefForwardingComponent
+} from "../../types";
+import {
+    Portal
+} from "react-native-portalize";
+
+/**
+ * A generic modal
+ * @param props {@link IModalProps}
+ * @returns Element
+ */
+const Modal: RefForwardingComponent<IModalRef, IModalProps> = ({
+    isDisabledOverlay = false,
+    isOverlayVisible = true,
+    alignContent = "center",
+    onOverlayPress,
+    contentStyle,
+    children,
+    style
+}, ref) => {
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const scaleAnim = useRef(new Animated.Value(0)).current;
+
+    useEffect(() => {
+        Animated.timing(scaleAnim, {
+            easing: Easing.out(Easing.back(1.5)),
+            useNativeDriver: true,
+            duration: 350,
+            toValue: 1
+        }).start();
+    }, []);
+
+    useImperativeHandle(
+        ref,
+        () => ({
+            closeAnimation
+        }),
+        []
+    );
+
+    const closeAnimation = (onClosed?: () => void) => {
+        Animated.timing(scaleAnim, {
+            easing: Easing.in(Easing.ease),
+            useNativeDriver: true,
+            duration: 250,
+            toValue: 0
+        }).start(({
+            finished
+        }) => {
+            if(finished) {
+                if(onClosed) onClosed();
+            }
+        });
+    };
+
+    const renderOverlay = () => {
+        if(!isOverlayVisible) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.overlay
+            ]}
+            onClick={() => {
+                if(isDisabledOverlay) {
+                    return;
+                }
+
+                if(onOverlayPress) onOverlayPress({
+                    closeAnimation
+                });
+            }}
+        />;
+    };
+
+    const renderContent = () => {
+        if(!children) {
+            return null;
+        }
+
+        return <View
+            style={[
+                contentStyle,
+                stylesheet.content
+            ]}
+        >
+            {children}
+        </View>;
+    };
+
+    return <Portal>
+        <Animated.View
+            style={[
+                style,
+                {
+                    justifyContent: alignContent === "center" ? "center" : undefined,
+                    alignItems: alignContent === "center" ? "center" : "baseline",
+                    backgroundColor: colors.system.scrim,
+                    transform: [{
+                        scale: scaleAnim
+                    }]
+                }
+            ]}
+        >
+            {renderOverlay()}
+            {renderContent()}
+        </Animated.View>
+    </Portal>;
+};
+export default forwardRef(Modal);

+ 28 - 0
src/components/modal/stylesheet.ts

@@ -0,0 +1,28 @@
+import {
+    StyleSheet
+} from "react-native";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        position: "static",
+        display: "flex",
+        zIndex: 99997,
+        bottom: 0,
+        right: 0,
+        left: 0,
+        top: 0
+    },
+    overlay: {
+        position: "static",
+        zIndex: 99998,
+        bottom: 0,
+        right: 0,
+        left: 0,
+        top: 0
+    },
+    content: {
+        display: "contents",
+        zIndex: 99999
+    }
+});
+export default stylesheet;

+ 32 - 0
src/components/modal/type.ts

@@ -0,0 +1,32 @@
+import {
+    type ReactNode
+} from "react";
+import {
+    type StyleProp,
+    type ViewStyle
+} from "react-native";
+
+export type IModalRef = {
+    closeAnimation: (onClosed?: () => void) => void;
+};
+
+export type ModalAlignContentProp = "center" | "free";
+
+export type ModalInternalProps = {
+    onOverlayPress?: (props: {
+        closeAnimation: (callback?: () => void) => void
+    }) => void;
+    contentStyle?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
+    style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
+    alignContent?: ModalAlignContentProp;
+    isDisabledOverlay?: boolean;
+    isOverlayVisible?: boolean;
+    children?: ReactNode;
+};
+
+interface IModalProps extends ModalInternalProps {
+};
+
+export type {
+    IModalProps as default
+};

+ 40 - 0
src/components/text/index.tsx

@@ -0,0 +1,40 @@
+import {
+    type FC
+} from "react";
+import {
+    Text as NativeText
+} from "react-native";
+import type ITextProps from "./type";
+import {
+    NCoreUIKitTheme
+} from "../..";
+
+const Text: FC<ITextProps> = ({
+    variant = "bodyMediumSize",
+    color = "mid",
+    customColor,
+    children,
+    style,
+    ...props
+}) => {
+    const {
+        typography,
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    return (
+        <NativeText
+            {...props}
+            style={[
+                style,
+                {
+                    ...typography[variant],
+                    color: customColor ? customColor : color ? colors.content.text[color] : colors.content.text.mid
+                }
+            ]}
+        >
+            {children}
+        </NativeText>
+    );
+};
+export default Text;

+ 18 - 0
src/components/text/type.ts

@@ -0,0 +1,18 @@
+import {
+    type ReactNode
+} from "react";
+import {
+    type TextStyle,
+    type StyleProp
+} from "react-native";
+
+interface ITextProps {
+    style?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    color?: keyof NCoreUIKit.TextContentColors;
+    variant?: keyof NCoreUIKit.Typography;
+    customColor?: string;
+    children?: ReactNode;
+}
+export type {
+    ITextProps as default
+};

+ 55 - 0
src/context/index.tsx

@@ -0,0 +1,55 @@
+import {
+    type ReactNode
+} from "react";
+import NCoreUIKitLocalize from "./localize";
+import NCoreUIKitModal from "./modal";
+import NCoreUIKitTheme from "./theme";
+import {
+    type NCoreUIKitConfig
+} from "../types";
+
+class CoreContext<T extends NCoreUIKitConfig> {
+    NCoreUIKitLocalize: NCoreUIKitLocalize<T>;
+    NCoreUIKitTheme: NCoreUIKitTheme<T>;
+    NCoreUIKitModal: NCoreUIKitModal;
+
+    constructor(initialState: T) {
+        this.NCoreUIKitTheme = new NCoreUIKitTheme({
+            initialSelectedActiveSharpness: initialState.initialSelectedActiveSharpness,
+            initialSelectedGapPropagation: initialState.initialSelectedGapPropagation,
+            initialSelectedPalette: initialState.initialSelectedPalette,
+            initialSelectedTheme: initialState.initialSelectedTheme,
+            projectThemes: initialState.projectThemes
+        });
+
+        this.NCoreUIKitLocalize = new NCoreUIKitLocalize({
+            initialSelectedLocale: initialState.initialSelectedLocale,
+            projectLocales: initialState.projectLocales
+        });
+
+        this.NCoreUIKitModal = new NCoreUIKitModal({
+            data: []
+        });
+    }
+
+    Provider = ({
+        children
+    }: {
+        children: ReactNode
+    }) => {
+        const LocalizeContext = this.NCoreUIKitLocalize;
+        const ModalContext = this.NCoreUIKitModal;
+        const ThemeContext = this.NCoreUIKitTheme;
+
+        return <ThemeContext.Provider>
+            <LocalizeContext.Provider>
+                <ModalContext.Provider>
+                    <ModalContext.Render>
+                        {children}
+                    </ModalContext.Render>
+                </ModalContext.Provider>
+            </LocalizeContext.Provider>
+        </ThemeContext.Provider>;
+    };
+};
+export default CoreContext;

+ 199 - 0
src/context/localize.tsx

@@ -0,0 +1,199 @@
+import {
+    type ReactElement,
+    type ReactNode,
+    useContext
+} from "react";
+import type ITextProps from "../components/text/type";
+import {
+    type LocalizeContextStateType,
+    type LocalizeContextType,
+    type LocalizeType
+} from "../types";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+import Text from "../components/text";
+import {
+    uuid
+} from "../utils";
+import defaultLocalesData from "../variants/locales/default.json";
+
+class NCoreUIKitLocalize<T extends LocalizeType> extends NCoreContext<LocalizeContextType, ConfigType<LocalizeContextType>> {
+    projectLocales?: NCoreUIKit.Locales = defaultLocalesData;
+
+    useContext = ({
+        activeLocale
+    }: {
+        activeLocale?: NCoreUIKit.LocaleKey
+    } = {}): LocalizeContextType => {
+        const currentState = useContext(this.stateContext);
+
+        if(activeLocale) {
+            const activeState = this.prepare({
+                activeLocale: activeLocale ? activeLocale : currentState.activeLocale
+            });
+
+            return {
+                ...currentState,
+                ...activeState,
+                localizeWithObject: this.localizeWithObject,
+                localize: this.localize
+            };
+        }
+
+        return {
+            ...currentState,
+            localizeWithObject: this.localizeWithObject,
+            localize: this.localize
+        };
+    };
+
+    constructor(initialState: T) {
+        const {
+            initialSelectedLocale,
+            projectLocales
+        } = initialState;
+
+        const initialLanguage = defaultLocalesData.find(l => l.locale === "tr-TR");
+
+        if(!initialLanguage) {
+            throw new Error("Initial Locale error!.");
+        }
+
+        super({
+            localizeWithObject: () => "Localization is loading...",
+            activeLocale: initialSelectedLocale ?? "tr-TR",
+            localize: () => "Localization is loading...",
+            translations: initialLanguage.translations,
+            isRTL: initialLanguage.isRTL
+        }, {
+            key: "NCoreUIKitLocalize"
+        });
+
+        this.projectLocales = projectLocales;
+
+        const activeState = this.prepare({
+            activeLocale: initialSelectedLocale ?? "tr-TR"
+        });
+
+        this.setState(activeState);
+    }
+
+    prepare = (props?: {
+        activeLocale?: NCoreUIKit.LocaleKey
+    }): LocalizeContextStateType => {
+        const locale = props && props.activeLocale ? props.activeLocale : this.state.activeLocale;
+
+        const defaultLocale = defaultLocalesData.find(l => l.locale === locale) ?? defaultLocalesData[0]!;
+
+        const defaultState: LocalizeContextStateType = {
+            activeLocale: defaultLocale.locale as NCoreUIKit.LocaleKey,
+            // localizeWithObject: this.localizeWithObject,
+            translations: defaultLocale.translations,
+            isRTL: defaultLocale.isRTL
+            // localize: this.localize
+        };
+
+        if(!this.projectLocales || (this.projectLocales && !this.projectLocales.length)) {
+            return defaultState;
+        }
+
+        const currentProjectLocale = this.projectLocales.find(l => l.locale === locale);
+
+        if(!currentProjectLocale) {
+            return defaultState;
+        }
+
+        const newState: LocalizeContextStateType = {
+            activeLocale: currentProjectLocale.locale as NCoreUIKit.LocaleKey,
+            // localizeWithObject: this.localizeWithObject,
+            isRTL: currentProjectLocale.isRTL,
+            // localize: this.localize,
+            translations: {
+                ...defaultLocale.translations,
+                ...currentProjectLocale.translations
+            }
+        };
+
+        return newState;
+    };
+
+    localizeWithObject = (
+        translation: keyof NCoreUIKit.Translation,
+        parameters?: Array<string | ReactNode | ReactElement>,
+        props?: Array<ITextProps>
+    ) => {
+        try {
+            let translatedText: string | ReactNode[] = String(this.state.translations[translation]);
+
+            if(!translatedText) {
+                return translation;
+            }
+
+            if(parameters && parameters.length) {
+                parameters.forEach((_item, index) => {
+                    if (typeof translatedText !== "string") return;
+
+                    translatedText = translatedText!.split(`{{${index}}}`).flatMap((item) => [
+                        item,
+                        props && props[index] ? <Text
+                            key={uuid()}
+                            {...props[index]}
+                        >{parameters[index]}</Text> : <Text key={uuid()}>{parameters[index]}</Text>
+                    ]).slice(0, -1);
+                });
+            }
+
+            return translatedText;
+        } catch(e) {
+            console.error(e);
+            return translation;
+        }
+    };
+
+    localize = (
+        translation: keyof NCoreUIKit.Translation,
+        parameters?: Array<string | number>
+    ) => {
+        try {
+            let translatedText = this.state.translations[translation];
+
+            if(!translatedText) {
+                return translation;
+            }
+
+            if(parameters && parameters.length) {
+                parameters.forEach((item, index) => {
+                    translatedText = translatedText.replace(`{{${index}}}`, String(item));
+                });
+            }
+
+            return translatedText;
+        } catch(e) {
+            console.error(e);
+            return translation;
+        }
+    };
+
+    switch = ({
+        localeKey
+    }: {
+        localeKey: NCoreUIKit.LocaleKey
+    }) => {
+        const activeState = this.prepare({
+            activeLocale: localeKey
+        });
+
+        this.setState(activeState);
+    };
+
+    updateProjectLocales = (newProjectLocales: NCoreUIKit.Locales, newState?: {
+        activeLocale?: NCoreUIKit.LocaleKey
+    }) => {
+        this.projectLocales = newProjectLocales;
+        const activeState = this.prepare(newState);
+
+        this.setState(activeState);
+    };
+}
+export default NCoreUIKitLocalize;

+ 106 - 0
src/context/modal.tsx

@@ -0,0 +1,106 @@
+import {
+    type ReactNode,
+    Fragment
+} from "react";
+import {
+    type ModalContextType,
+    type ModalDataType
+} from "../types/modal";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+import {
+    Host
+} from "react-native-portalize";
+import Modal from "../components/modal";
+
+class NCoreUIKitModal extends NCoreContext<ModalContextType, ConfigType<ModalContextType>> {
+    constructor({
+        data = []
+    }: {
+        data?: Array<ModalDataType>
+    }) {
+        super({
+            close: () => {},
+            open: () => {},
+            data: data
+        }, {
+            key: "NCoreUIKit-ModalContext"
+        });
+    };
+
+    open = (modalData: ModalDataType) => {
+        const currentData = this.state.data;
+
+        currentData.push(modalData);
+
+        this.setState({
+            data: currentData
+        });
+    };
+
+    close = (props?: {
+        index?: number;
+        id?: string;
+    }) => {
+        const currentData = this.state.data;
+
+        if (props && props.id) {
+            const keyIndex = currentData.findIndex((modal) => modal.id === props.id);
+
+            if (keyIndex !== -1) {
+                currentData.splice(keyIndex, 1);
+
+                this.setState({
+                    data: currentData
+                });
+            }
+
+            return;
+        }
+
+        if (props && props.index !== undefined) {
+            currentData.splice(props.index, 1);
+
+            this.setState({
+                data: currentData
+            });
+
+            return;
+        }
+
+        currentData.pop();
+
+        this.setState({
+            data: currentData
+        });
+    };
+
+    Render = ({
+        children
+    }: {
+        children: ReactNode;
+    }) => {
+        const {
+            data
+        } = this.useContext();
+
+        return <Fragment>
+            <Host>
+                {children}
+                {data.map((item: ModalDataType) => {
+                    return <Modal
+                        key={`NCoreUIKit-Modal-${item.id}`}
+                        onOverlayPress={item.onOverlayPress ? item.onOverlayPress : () => {
+                            this.close({
+                                id: item.id
+                            });
+                        }}
+                        {...item}
+                    />;
+                })}
+            </Host>
+        </Fragment>;
+    };
+}
+export default NCoreUIKitModal;

+ 211 - 0
src/context/theme.tsx

@@ -0,0 +1,211 @@
+import {
+    useContext
+} from "react";
+import {
+    type ThemeContextStateType,
+    type GapPropagationType,
+    type ThemeContextType,
+    type SharpnessType,
+    type ThemesType
+} from "../types";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+import {
+    mergeTypographyTokens,
+    mergePalettes,
+    mergeShapes
+} from "../helpers";
+import defaultPaletteData from "../variants/themes/default.json";
+
+class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextType, ConfigType<ThemeContextType>> {
+    projectThemes?: NCoreUIKit.Palette = defaultPaletteData;
+
+    useContext = ({
+        gapPropagation,
+        paletteKey,
+        sharpness,
+        themeKey
+    }: {
+        gapPropagation?: GapPropagationType;
+        paletteKey?: NCoreUIKit.PaletteKey;
+        themeKey?: NCoreUIKit.ThemeKey;
+        sharpness?: SharpnessType;
+    } = {}): ThemeContextType => {
+        const currentState = useContext(this.stateContext);
+
+        if(
+            gapPropagation ||
+            paletteKey ||
+            sharpness ||
+            themeKey
+        ) {
+            const activeState = this.prepare({
+                activeGapPropagation: gapPropagation ? gapPropagation : currentState.activeGapPropagation,
+                activeSharpness: sharpness ? sharpness : currentState.activeSharpness,
+                activePalette: paletteKey ? paletteKey : currentState.activePalette,
+                activeTheme: themeKey ? themeKey : currentState.activeTheme
+            });
+
+            return {
+                ...currentState,
+                ...activeState
+            };
+        }
+
+        return currentState;
+    };
+
+    constructor(initialState: T) {
+        const {
+            projectThemes,
+            initialSelectedActiveSharpness,
+            initialSelectedGapPropagation,
+            initialSelectedPalette,
+            initialSelectedTheme
+        } = initialState;
+
+        const initialPalette = defaultPaletteData.palettes.find(p => p.name === "nibgat");
+
+        if(!initialPalette) {
+            throw new Error("Initial Theme error!.");
+        }
+
+        const initialTheme = initialPalette.themes[initialSelectedTheme as keyof typeof initialPalette.themes];
+
+        super({
+            activeGapPropagation: initialSelectedGapPropagation ?? "compact",
+            activeSharpness: initialSelectedActiveSharpness ?? "soft",
+            inlineSpaces: defaultPaletteData.shapes.inlineSpaces,
+            typography: defaultPaletteData.typography.spacious,
+            radiuses: defaultPaletteData.shapes.radiuses.soft,
+            spaces: defaultPaletteData.shapes.spaces.spacious,
+            activePalette: initialSelectedPalette ?? "nibgat",
+            activeTheme: initialSelectedTheme ?? "dark",
+            borders: defaultPaletteData.shapes.borders,
+            colors: initialTheme
+        }, {
+            key: "NCoreUIKitTheme"
+        });
+
+        this.projectThemes = projectThemes;
+
+        const activeState = this.prepare({
+            activeGapPropagation: initialSelectedGapPropagation ?? "compact",
+            activeSharpness: initialSelectedActiveSharpness ?? "soft",
+            activePalette: initialSelectedPalette ?? "nibgat",
+            activeTheme: initialSelectedTheme ?? "dark"
+        });
+        this.setState(activeState);
+    }
+
+    prepare = (props?: {
+        activeGapPropagation?: GapPropagationType;
+        activePalette?: NCoreUIKit.PaletteKey;
+        activeTheme?: NCoreUIKit.ThemeKey;
+        activeSharpness?: SharpnessType;
+    }): ThemeContextStateType => {
+        const gapPropagation = props && props.activeGapPropagation ? props.activeGapPropagation : this.state.activeGapPropagation;
+        const sharpness = props && props.activeSharpness ? props.activeSharpness : this.state.activeSharpness;
+        const palette = props && props.activePalette ? props.activePalette : this.state.activePalette;
+        const theme = props && props.activeTheme ? props.activeTheme : this.state.activeTheme;
+
+        const defaultPalette = defaultPaletteData.palettes.find(p => p.name === palette) ?? defaultPaletteData.palettes[0];
+        const defaultTheme = defaultPalette.themes[theme as keyof typeof defaultPalette.themes] ?? defaultPalette.themes.dark;
+
+        const defaultState: ThemeContextStateType = {
+            typography: defaultPaletteData.typography[gapPropagation],
+            spaces: defaultPaletteData.shapes.spaces[gapPropagation],
+            radiuses: defaultPaletteData.shapes.radiuses[sharpness],
+            inlineSpaces: defaultPaletteData.shapes.inlineSpaces,
+            borders: defaultPaletteData.shapes.borders,
+            activeGapPropagation: gapPropagation,
+            activeSharpness: sharpness,
+            activePalette: palette,
+            colors: defaultTheme,
+            activeTheme: theme
+        };
+
+        if(!this.projectThemes || (this.projectThemes && !this.projectThemes.palettes.length)) {
+            return defaultState;
+        }
+
+        const currentProjectPalette = this.projectThemes.palettes.find(p => p.name === palette);
+
+        if(!currentProjectPalette) {
+            return defaultState;
+        }
+
+        const currentProjectTheme = currentProjectPalette.themes[theme];
+
+        if(!currentProjectTheme) {
+            return defaultState;
+        }
+
+        const shapes = mergeShapes({
+            projectShapes: this.projectThemes.shapes,
+            defaultShapes: defaultPaletteData.shapes
+        });
+
+        const typography = mergeTypographyTokens({
+            defaultTypographyTokens: defaultPaletteData.typography,
+            projectTypographyTokens: this.projectThemes.typography
+        });
+
+        const colors = mergePalettes(
+            defaultPaletteData.palettes,
+            this.projectThemes.palettes,
+            palette,
+            theme
+        );
+
+        const newState: ThemeContextStateType = {
+            typography: typography[gapPropagation],
+            spaces: shapes.spaces[gapPropagation],
+            activeGapPropagation: gapPropagation,
+            radiuses: shapes.radiuses[sharpness],
+            inlineSpaces: shapes.inlineSpaces,
+            activeSharpness: sharpness,
+            borders: shapes.borders,
+            activePalette: palette,
+            activeTheme: theme,
+            colors: colors
+        };
+
+        return newState;
+    };
+
+    switch = ({
+        gapPropagation,
+        paletteKey,
+        sharpness,
+        themeKey
+    }: {
+        gapPropagation?: GapPropagationType;
+        paletteKey?: NCoreUIKit.PaletteKey;
+        themeKey?: NCoreUIKit.ThemeKey;
+        sharpness?: SharpnessType;
+    }) => {
+        const activeState = this.prepare({
+            activeGapPropagation: gapPropagation ?? this.state.activeGapPropagation,
+            activeSharpness: sharpness ?? this.state.activeSharpness,
+            activePalette: paletteKey ?? this.state.activePalette,
+            activeTheme: themeKey ?? this.state.activeTheme
+        });
+
+        this.setState(activeState);
+    };
+
+    updateProjectTheme = (newProjectThemes: NCoreUIKit.Palette, newState?: {
+        activeGapPropagation?: GapPropagationType;
+        activePalette?: NCoreUIKit.PaletteKey;
+        activeTheme?: NCoreUIKit.ThemeKey;
+        activeSharpness?: SharpnessType;
+    }) => {
+        this.projectThemes = newProjectThemes;
+        const activeState = this.prepare(newState);
+
+        this.setState(activeState);
+    };
+}
+export default NCoreUIKitTheme;

+ 16 - 0
src/helpers/index.ts

@@ -0,0 +1,16 @@
+export {
+    mergeRadiuses,
+    mergeBorders,
+    mergeSpaces,
+    mergeShapes
+} from "./theme/shapes";
+
+export {
+    mergeTypographyTokens,
+    mergeTypography
+} from "./theme/typography";
+
+export {
+    mergePalettes,
+    mergeTheme
+} from "./theme/palette";

+ 76 - 0
src/helpers/theme/palette.ts

@@ -0,0 +1,76 @@
+import {
+    type RecursiveRecord
+} from "../../types";
+
+const mergeCurrent = (defaultJSON: RecursiveRecord, projectJSON: RecursiveRecord) => {
+    const projectKeys: Array<keyof typeof projectJSON> = Object.keys(projectJSON);
+
+    const newJSON = JSON.parse(JSON.stringify(defaultJSON));
+
+    projectKeys.forEach((pKey) => {
+        if(typeof projectJSON[pKey] === "string") {
+            newJSON[pKey] = projectJSON[pKey];
+        } else {
+            if(typeof defaultJSON[pKey] !== "string") {
+                if(defaultJSON[pKey]) {
+                    newJSON[pKey] = mergeCurrent(defaultJSON[pKey], projectJSON[pKey]);
+                } else {
+                    newJSON[pKey] = projectJSON[pKey];
+                }
+            } else {
+                throw new Error("The project theme scheme is not valid.");
+            }
+        }
+    });
+
+    return newJSON;
+};
+
+export const mergeTheme = (
+    defaultTheme: NCoreUIKit.ThemeTokens,
+    projectTheme: NCoreUIKit.ThemeTokens
+): NCoreUIKit.ThemeTokens => {
+    return {
+        content: mergeCurrent(defaultTheme.content, projectTheme.content),
+        system: mergeCurrent(defaultTheme.system, projectTheme.system)
+    };
+};
+
+export const mergePalettes = (
+    defaultPalettes: NCoreUIKit.ThemePalettes,
+    projectPalettes: NCoreUIKit.ThemePalettes,
+    activePalette: NCoreUIKit.PaletteKey,
+    activeTheme: NCoreUIKit.ThemeKey
+): NCoreUIKit.ThemeTokens => {
+    const defaultPalette = defaultPalettes.find(p => p.name === activePalette);
+
+    if(!defaultPalette) {
+        throw new Error("Palette not found!.");
+    }
+
+    const projectPalette = projectPalettes.find(p => p.name === activePalette);
+
+    if(projectPalette) {
+        const defaultThemeKey = Object.keys(defaultPalette.themes).find(t => t === activeTheme) as keyof typeof defaultPalette.themes;
+
+        if(!defaultThemeKey) {
+            throw new Error("Theme not found!.");
+        }
+
+        const defaultTheme = defaultPalette.themes[defaultThemeKey as keyof typeof defaultPalette.themes] as unknown as NCoreUIKit.ThemeTokens;
+
+        const projectThemeKey = Object.keys(projectPalette.themes).find(t => t === activeTheme) as keyof typeof projectPalette.themes;
+
+        if(projectThemeKey) {
+            const projectTheme = projectPalette.themes[projectThemeKey] as unknown as NCoreUIKit.ThemeTokens;
+
+            return mergeTheme(defaultTheme, projectTheme);
+        }
+
+        return defaultTheme;
+    }
+
+    const activeT = defaultPalette.themes[activeTheme as keyof typeof defaultPalette.themes] as unknown as NCoreUIKit.ThemeTokens;
+
+    return activeT;
+};

+ 106 - 0
src/helpers/theme/shapes.ts

@@ -0,0 +1,106 @@
+import defaultJSON from "../../variants/themes/default.json";
+
+export const mergeSpaces = (
+    defaultSpacesTokens: NCoreUIKit.Spaces,
+    projectSpacesTokens: NCoreUIKit.Spaces
+) => {
+    return {
+        ...defaultSpacesTokens,
+        ...projectSpacesTokens
+    };
+};
+
+export const mergeRadiuses = (
+    defaultRadiusesTokens: NCoreUIKit.Radiuses,
+    projectRadiusesTokens: NCoreUIKit.Radiuses
+) => {
+    return {
+        ...defaultRadiusesTokens,
+        ...projectRadiusesTokens
+    };
+};
+
+export const mergeBorders = (
+    defaultBordersTokens: NCoreUIKit.Borders,
+    projectBordersTokens: NCoreUIKit.Borders
+) => {
+    return {
+        ...defaultBordersTokens,
+        ...projectBordersTokens
+    };
+};
+
+export const mergeInlineSpaces = (
+    defaultInlineSpacesTokens: NCoreUIKit.InlineSpaces,
+    projectInlineSpacesTokens: NCoreUIKit.InlineSpaces
+) => {
+    return {
+        ...defaultInlineSpacesTokens,
+        ...projectInlineSpacesTokens
+    };
+};
+
+export const mergeShapes = ({
+    defaultShapes,
+    projectShapes
+}: {
+    defaultShapes: NCoreUIKit.Shapes;
+    projectShapes: NCoreUIKit.Shapes;
+}) => {
+    const defaultGapPropagations = Object.keys(defaultJSON.shapes.spaces) as (keyof NCoreUIKit.SpacesWithVariants)[];
+    const defaultSharpness = Object.keys(defaultJSON.shapes.radiuses) as (keyof NCoreUIKit.RadiusesWithVariants)[];
+
+    const defaultInlineSpaces = defaultShapes.inlineSpaces;
+    const defaultRadiuses = defaultShapes.radiuses;
+    const defaultBorders = defaultShapes.borders;
+    const defaultSpaces = defaultShapes.spaces;
+
+    const newShapes: NCoreUIKit.Shapes = {
+        inlineSpaces: defaultInlineSpaces,
+        radiuses: defaultRadiuses,
+        borders: defaultBorders,
+        spaces: defaultSpaces
+    };
+
+    if(!projectShapes) {
+        return newShapes;
+    }
+
+    if(projectShapes.borders) {
+        const projectBorders = projectShapes.borders;
+
+        newShapes.borders = mergeBorders(defaultBorders, projectBorders);
+    }
+
+    if(projectShapes.inlineSpaces) {
+        const projectInlineSpaces = projectShapes.inlineSpaces;
+
+        newShapes.inlineSpaces = mergeInlineSpaces(defaultInlineSpaces, projectInlineSpaces);
+    }
+
+    if(projectShapes.spaces) {
+        defaultGapPropagations.forEach((gP) => {
+            if(projectShapes.spaces[gP]) {
+                if(defaultSpaces[gP]) {
+                    newShapes.spaces[gP] = mergeSpaces(defaultSpaces[gP], projectShapes.spaces[gP]);
+                } else {
+                    newShapes.spaces[gP] = projectShapes.spaces[gP];
+                }
+            }
+        });
+    }
+
+    if(projectShapes.radiuses) {
+        defaultSharpness.forEach((s) => {
+            if(projectShapes.radiuses[s]) {
+                if(defaultRadiuses[s]) {
+                    newShapes.radiuses[s] = mergeRadiuses(defaultRadiuses[s], projectShapes.radiuses[s]);
+                } else {
+                    newShapes.radiuses[s] = projectShapes.radiuses[s];
+                }
+            }
+        });
+    }
+
+    return newShapes;
+};

+ 41 - 0
src/helpers/theme/typography.ts

@@ -0,0 +1,41 @@
+import defaultJSON from "../../variants/themes/default.json";
+
+export const mergeTypography = (
+    defaultSpacesTokens: NCoreUIKit.Typography,
+    projectSpacesTokens: NCoreUIKit.Typography
+) => {
+    return {
+        ...defaultSpacesTokens,
+        ...projectSpacesTokens
+    };
+};
+
+export const mergeTypographyTokens = ({
+    defaultTypographyTokens,
+    projectTypographyTokens
+}: {
+    defaultTypographyTokens: NCoreUIKit.TypographyWithVariants;
+    projectTypographyTokens: NCoreUIKit.TypographyWithVariants;
+}) => {
+    const defaultGapPropagations = Object.keys(defaultJSON.shapes.spaces) as (keyof NCoreUIKit.TypographyWithVariants)[];
+
+    const defaultTypography = defaultTypographyTokens;
+
+    const newTypography: NCoreUIKit.TypographyWithVariants = defaultTypography;
+
+    if(!projectTypographyTokens) {
+        return newTypography;
+    }
+
+    defaultGapPropagations.forEach((gP) => {
+        if(projectTypographyTokens[gP]) {
+            if(defaultTypography[gP]) {
+                newTypography[gP] = mergeTypography(defaultTypography[gP], projectTypographyTokens[gP]);
+            } else {
+                newTypography[gP] = projectTypographyTokens[gP];
+            }
+        }
+    });
+
+    return newTypography;
+};

+ 10 - 11
src/index.tsx

@@ -1,7 +1,11 @@
 import {
-    ReactNode
+    type ReactNode
 } from "react";
-import useStyles from "./stylesheet";
+import type {
+    NCoreUIKitConfig,
+    LocalizeType,
+    ThemesType
+} from "./types";
 import CoreContext from "./context";
 import {
     default as NCoreUIKitLocalizeClass
@@ -12,11 +16,6 @@ import {
 import {
     default as NCoreUIKitThemeClass
 } from "./context/theme";
-import {
-    NCoreUIKitConfig,
-    LocalizeType,
-    ThemesType
-} from "./types";
 
 class NCoreUIKitBase<T extends NCoreUIKitConfig> {
     NCoreUIKitContext: CoreContext<T>;
@@ -59,16 +58,16 @@ export const setupNCoreUIKit = (initialState: NCoreUIKitConfig, isCustom: boolea
 };
 
 export {
-    TextInput,
+    // TextInput,
     Loading,
-    Dialog,
-    Button,
+    // Dialog,
+    // Button,
     Modal,
     Text
 } from "./components";
 
 export type {
-    IDialogRef,
+    // IDialogRef,
     IModalRef
 } from "./components";
 

+ 6 - 3
src/types/icon.ts

@@ -1,11 +1,14 @@
-import type {
-    FC
+import {
+    type FC
 } from "react";
+import {
+    type SvgProps
+} from "react-native-svg";
 
 export interface INCoreUIKitIconProps {
     color?: keyof NCoreUIKit.IconContentColors;
+    style?: SvgProps["style"];
     customColor?: string;
-    className?: string;
     size?: number;
 }
 

+ 17 - 14
src/types/index.ts

@@ -1,20 +1,23 @@
 import {
-    ForwardRefRenderFunction
+    type ForwardRefRenderFunction
 } from "react";
 import {
-    LocalizeType,
-    LocaleType
+    type TextStyle
+} from "react-native";
+import {
+    type LocalizeType,
+    type LocaleType
 } from "./locale";
 import {
-    ModalDataType,
-    ModalType
+    type ModalDataType,
+    type ModalType
 } from "./modal";
 import {
-    GapPropagationType,
-    SharpnessType,
-    PaletteType,
-    ThemesType,
-    ThemeType
+    type GapPropagationType,
+    type SharpnessType,
+    type PaletteType,
+    type ThemesType,
+    type ThemeType
 } from "./theme";
 import defaultLocaleJSON from "../variants/locales/default.json";
 import defaultThemeJSON from "../variants/themes/default.json";
@@ -116,11 +119,11 @@ declare global {
         interface Typography extends DefaultTypography {}
 
         interface TypographyToken {
-            letterSpacing: string | number;
-            lineHeight: string | number;
-            fontWeight: string | number;
-            fontSize: string | number;
+            fontWeight: TextStyle["fontWeight"];
+            letterSpacing: number;
             fontFamily: string;
+            lineHeight: number;
+            fontSize: number;
         }
 
         type ThemePalettes = Array<ThemePalette>;

+ 2 - 2
src/types/locale.ts

@@ -1,6 +1,6 @@
 import {
-    ReactElement,
-    ReactNode
+    type ReactElement,
+    type ReactNode
 } from "react";
 import ITextProps from "../components/text/text.types";
 

+ 1 - 1
src/types/modal.ts

@@ -1,5 +1,5 @@
 import {
-    ModalInternalProps
+    type ModalInternalProps
 } from "../components/modal/modal.types";
 
 export type ModalType = {

+ 61 - 61
src/variants/themes/default.json

@@ -69,214 +69,214 @@
         "spacious": {
             "displayLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 57,
-                "lineHeight": "64px",
+                "lineHeight": 64,
                 "letterSpacing": -0.25
             },
             "displayMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 45,
-                "lineHeight": "52px",
+                "lineHeight": 52,
                 "letterSpacing": 0
             },
             "displaySmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 36,
-                "lineHeight": "44px",
+                "lineHeight": 44,
                 "letterSpacing": 0
             },
             "headlineLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 32,
-                "lineHeight": "40px",
+                "lineHeight": 40,
                 "letterSpacing": 0
             },
             "headlineMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 28,
-                "lineHeight": "36px",
+                "lineHeight": 36,
                 "letterSpacing": 0
             },
             "headlineSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 24,
-                "lineHeight": "32px",
+                "lineHeight": 32,
                 "letterSpacing": 0
             },
             "titleLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 22,
-                "lineHeight": "28px",
+                "lineHeight": 28,
                 "letterSpacing": 0
             },
             "titleMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 16,
-                "lineHeight": "24px",
+                "lineHeight": 24,
                 "letterSpacing": 0.15
             },
             "titleSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 14,
-                "lineHeight": "20px",
+                "lineHeight": 20,
                 "letterSpacing": 0.1
             },
             "bodyLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 16,
-                "lineHeight": "24px",
+                "lineHeight": 24,
                 "letterSpacing": 0.5
             },
             "bodyMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 14,
-                "lineHeight": "20px",
+                "lineHeight": 20,
                 "letterSpacing": 0.25
             },
             "bodySmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 12,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.4
             },
             "labelLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 14,
-                "lineHeight": "20px",
+                "lineHeight": 20,
                 "letterSpacing": 0.1
             },
             "labelMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 12,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             },
             "labelSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 11,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             }
         },
         "compact": {
             "displayLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 45,
-                "lineHeight": "52px",
+                "lineHeight": 52,
                 "letterSpacing": 0
             },
             "displayMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 36,
-                "lineHeight": "44px",
+                "lineHeight": 44,
                 "letterSpacing": 0
             },
             "displaySmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Light",
+                "fontWeight": "300",
                 "fontSize": 32,
-                "lineHeight": "40px",
+                "lineHeight": 40,
                 "letterSpacing": 0
             },
             "headlineLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 28,
-                "lineHeight": "36px",
+                "lineHeight": 36,
                 "letterSpacing": 0
             },
             "headlineMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 24,
-                "lineHeight": "32px",
+                "lineHeight": 32,
                 "letterSpacing": 0
             },
             "headlineSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 22,
-                "lineHeight": "28px",
+                "lineHeight": 28,
                 "letterSpacing": 0
             },
             "titleLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 16,
-                "lineHeight": "24px",
+                "lineHeight": 24,
                 "letterSpacing": 0.15
             },
             "titleMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 14,
-                "lineHeight": "20px",
+                "lineHeight": 20,
                 "letterSpacing": 0.1
             },
             "titleSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Medium",
+                "fontWeight": "500",
                 "fontSize": 12,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.4
             },
             "bodyLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 14,
-                "lineHeight": "20px",
+                "lineHeight": 20,
                 "letterSpacing": 0.25
             },
             "bodyMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 12,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.4
             },
             "bodySmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 11,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             },
             "labelLargeSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 12,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             },
             "labelMediumSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 11,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             },
             "labelSmallSize": {
                 "fontFamily": "Geist",
-                "fontWeight": "Regular",
+                "fontWeight": "400",
                 "fontSize": 11,
-                "lineHeight": "16px",
+                "lineHeight": 16,
                 "letterSpacing": 0.5
             }
         }
@@ -664,4 +664,4 @@
             }
         }
     ]
-}
+}

+ 142 - 17
yarn.lock

@@ -4304,7 +4304,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/react@npm:^19.2.14":
+"@types/react@npm:19.2.14":
   version: 19.2.14
   resolution: "@types/react@npm:19.2.14"
   dependencies:
@@ -5519,7 +5519,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"babel-plugin-module-resolver@npm:^5.0.2":
+"babel-plugin-module-resolver@npm:5.0.2":
   version: 5.0.2
   resolution: "babel-plugin-module-resolver@npm:5.0.2"
   dependencies:
@@ -5877,6 +5877,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"boolbase@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "boolbase@npm:1.0.0"
+  checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf
+  languageName: node
+  linkType: hard
+
 "bplist-creator@npm:0.1.0":
   version: 0.1.0
   resolution: "bplist-creator@npm:0.1.0"
@@ -7150,6 +7157,36 @@ __metadata:
   languageName: node
   linkType: hard
 
+"css-select@npm:^5.1.0":
+  version: 5.2.2
+  resolution: "css-select@npm:5.2.2"
+  dependencies:
+    boolbase: "npm:^1.0.0"
+    css-what: "npm:^6.1.0"
+    domhandler: "npm:^5.0.2"
+    domutils: "npm:^3.0.1"
+    nth-check: "npm:^2.0.1"
+  checksum: 10c0/d79fffa97106007f2802589f3ed17b8c903f1c961c0fc28aa8a051eee0cbad394d8446223862efd4c1b40445a6034f626bb639cf2035b0bfc468544177593c99
+  languageName: node
+  linkType: hard
+
+"css-tree@npm:^1.1.3":
+  version: 1.1.3
+  resolution: "css-tree@npm:1.1.3"
+  dependencies:
+    mdn-data: "npm:2.0.14"
+    source-map: "npm:^0.6.1"
+  checksum: 10c0/499a507bfa39b8b2128f49736882c0dd636b0cd3370f2c69f4558ec86d269113286b7df469afc955de6a68b0dba00bc533e40022a73698081d600072d5d83c1c
+  languageName: node
+  linkType: hard
+
+"css-what@npm:^6.1.0":
+  version: 6.2.2
+  resolution: "css-what@npm:6.2.2"
+  checksum: 10c0/91e24c26fb977b4ccef30d7007d2668c1c10ac0154cc3f42f7304410e9594fb772aea4f30c832d2993b132ca8d99338050866476210316345ec2e7d47b248a56
+  languageName: node
+  linkType: hard
+
 "csstype@npm:^3.2.2":
   version: 3.2.3
   resolution: "csstype@npm:3.2.3"
@@ -7527,6 +7564,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"dom-serializer@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "dom-serializer@npm:2.0.0"
+  dependencies:
+    domelementtype: "npm:^2.3.0"
+    domhandler: "npm:^5.0.2"
+    entities: "npm:^4.2.0"
+  checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2
+  languageName: node
+  linkType: hard
+
 "dom-walk@npm:^0.1.0":
   version: 0.1.2
   resolution: "dom-walk@npm:0.1.2"
@@ -7541,6 +7589,33 @@ __metadata:
   languageName: node
   linkType: hard
 
+"domelementtype@npm:^2.3.0":
+  version: 2.3.0
+  resolution: "domelementtype@npm:2.3.0"
+  checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9
+  languageName: node
+  linkType: hard
+
+"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
+  version: 5.0.3
+  resolution: "domhandler@npm:5.0.3"
+  dependencies:
+    domelementtype: "npm:^2.3.0"
+  checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a
+  languageName: node
+  linkType: hard
+
+"domutils@npm:^3.0.1":
+  version: 3.2.2
+  resolution: "domutils@npm:3.2.2"
+  dependencies:
+    dom-serializer: "npm:^2.0.0"
+    domelementtype: "npm:^2.3.0"
+    domhandler: "npm:^5.0.3"
+  checksum: 10c0/47938f473b987ea71cd59e59626eb8666d3aa8feba5266e45527f3b636c7883cca7e582d901531961f742c519d7514636b7973353b648762b2e3bedbf235fada
+  languageName: node
+  linkType: hard
+
 "dot-prop@npm:^5.1.0":
   version: 5.3.0
   resolution: "dot-prop@npm:5.3.0"
@@ -7692,6 +7767,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"entities@npm:^4.2.0":
+  version: 4.5.0
+  resolution: "entities@npm:4.5.0"
+  checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
+  languageName: node
+  linkType: hard
+
 "env-paths@npm:^2.2.0":
   version: 2.2.1
   resolution: "env-paths@npm:2.2.1"
@@ -8119,7 +8201,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-plugin-react-native@npm:^5.0.0":
+"eslint-plugin-react-native@npm:5.0.0":
   version: 5.0.0
   resolution: "eslint-plugin-react-native@npm:5.0.0"
   dependencies:
@@ -9534,6 +9616,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"globals@npm:17.4.0":
+  version: 17.4.0
+  resolution: "globals@npm:17.4.0"
+  checksum: 10c0/2be9e8c2b9035836f13d420b22f0247a328db82967d3bebfc01126d888ed609305f06c05895914e969653af5c6ba35fd7a0920f3e6c869afa60666c810630feb
+  languageName: node
+  linkType: hard
+
 "globals@npm:^14.0.0":
   version: 14.0.0
   resolution: "globals@npm:14.0.0"
@@ -9541,13 +9630,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"globals@npm:^17.4.0":
-  version: 17.4.0
-  resolution: "globals@npm:17.4.0"
-  checksum: 10c0/2be9e8c2b9035836f13d420b22f0247a328db82967d3bebfc01126d888ed609305f06c05895914e969653af5c6ba35fd7a0920f3e6c869afa60666c810630feb
-  languageName: node
-  linkType: hard
-
 "globalthis@npm:^1.0.4":
   version: 1.0.4
   resolution: "globalthis@npm:1.0.4"
@@ -11925,6 +12007,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"mdn-data@npm:2.0.14":
+  version: 2.0.14
+  resolution: "mdn-data@npm:2.0.14"
+  checksum: 10c0/67241f8708c1e665a061d2b042d2d243366e93e5bf1f917693007f6d55111588b952dcbfd3ea9c2d0969fb754aad81b30fdcfdcc24546495fc3b24336b28d4bd
+  languageName: node
+  linkType: hard
+
 "mdurl@npm:^1.0.0":
   version: 1.0.1
   resolution: "mdurl@npm:1.0.1"
@@ -12897,7 +12986,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ncore-context@npm:1.0.5":
+"ncore-context@npm:1.0.5, ncore-context@npm:^1.0.5":
   version: 1.0.5
   resolution: "ncore-context@npm:1.0.5"
   dependencies:
@@ -12918,11 +13007,14 @@ __metadata:
     "@types/react-native": "npm:0.73.0"
     expo: "npm:~55.0.4"
     expo-status-bar: "npm:~55.0.4"
+    ncore-context: "npm:^1.0.5"
     react: "npm:19.2.0"
     react-dom: "npm:19.2.0"
     react-native: "npm:0.83.2"
     react-native-builder-bob: "npm:0.40.13"
     react-native-monorepo-config: "npm:0.3.3"
+    react-native-portalize: "npm:^1.0.7"
+    react-native-svg: "npm:^15.15.3"
     react-native-web: "npm:~0.21.0"
   languageName: unknown
   linkType: soft
@@ -12934,8 +13026,8 @@ __metadata:
     "@react-native/babel-preset": "npm:0.83.0"
     "@release-it/conventional-changelog": "npm:10.0.1"
     "@types/eslint-plugin-jsx-a11y": "npm:6"
-    "@types/react": "npm:^19.2.14"
-    babel-plugin-module-resolver: "npm:^5.0.2"
+    "@types/react": "npm:19.2.14"
+    babel-plugin-module-resolver: "npm:5.0.2"
     del-cli: "npm:6.0.0"
     eslint: "npm:9.37.0"
     eslint-import-resolver-typescript: "npm:4.4.4"
@@ -12945,15 +13037,16 @@ __metadata:
     eslint-plugin-local-rules: "npm:3.0.2"
     eslint-plugin-react: "npm:7.37.5"
     eslint-plugin-react-hooks: "npm:7.0.0"
-    eslint-plugin-react-native: "npm:^5.0.0"
-    globals: "npm:^17.4.0"
+    eslint-plugin-react-native: "npm:5.0.0"
+    globals: "npm:17.4.0"
     jsonc-eslint-parser: "npm:2.4.1"
     lucide-react-native: "npm:0.577.0"
     ncore-context: "npm:1.0.5"
     react: "npm:19.2.0"
     react-native: "npm:0.83.2"
     react-native-builder-bob: "npm:0.40.13"
-    react-native-portalize: "npm:^1.0.7"
+    react-native-portalize: "npm:1.0.7"
+    react-native-svg: "npm:15.15.3"
     release-it: "npm:19.0.4"
     turbo: "npm:2.5.6"
     typescript: "npm:5.9.3"
@@ -12963,6 +13056,8 @@ __metadata:
     ncore-context: ">= 1.0.5"
     react: "*"
     react-native: "*"
+    react-native-portalize: ">= 1.0.7"
+    react-native-svg: ">= 15.15.3"
   languageName: unknown
   linkType: soft
 
@@ -13211,6 +13306,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"nth-check@npm:^2.0.1":
+  version: 2.1.1
+  resolution: "nth-check@npm:2.1.1"
+  dependencies:
+    boolbase: "npm:^1.0.0"
+  checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479
+  languageName: node
+  linkType: hard
+
 "nullthrows@npm:^1.1.1":
   version: 1.1.1
   resolution: "nullthrows@npm:1.1.1"
@@ -14464,7 +14568,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-native-portalize@npm:^1.0.7":
+"react-native-portalize@npm:1.0.7, react-native-portalize@npm:^1.0.7":
   version: 1.0.7
   resolution: "react-native-portalize@npm:1.0.7"
   peerDependencies:
@@ -14474,6 +14578,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-native-svg@npm:15.15.3, react-native-svg@npm:^15.15.3":
+  version: 15.15.3
+  resolution: "react-native-svg@npm:15.15.3"
+  dependencies:
+    css-select: "npm:^5.1.0"
+    css-tree: "npm:^1.1.3"
+    warn-once: "npm:0.1.1"
+  peerDependencies:
+    react: "*"
+    react-native: "*"
+  checksum: 10c0/6aa5ea4745c7d174909468b5d5cb75059856d907f0b039bf3650deb81c9afcbd356f2382d7a7424d4cc69597aea5a508484ee5dba91b9126e3b520bc10faefe9
+  languageName: node
+  linkType: hard
+
 "react-native-web@npm:~0.21.0":
   version: 0.21.2
   resolution: "react-native-web@npm:0.21.2"
@@ -17254,6 +17372,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"warn-once@npm:0.1.1":
+  version: 0.1.1
+  resolution: "warn-once@npm:0.1.1"
+  checksum: 10c0/f531e7b2382124f51e6d8f97b8c865246db8ab6ff4e53257a2d274e0f02b97d7201eb35db481843dc155815e154ad7afb53b01c4d4db15fb5aa073562496aff7
+  languageName: node
+  linkType: hard
+
 "watchpack-chokidar2@npm:^2.0.1":
   version: 2.0.1
   resolution: "watchpack-chokidar2@npm:2.0.1"