diff --git a/package.json b/package.json
index 59fef4e..accd399 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"react-cookie": "^7.2.2",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react-i18next": "^15.1.0",
+ "reactvvm": "^1.1.0",
"reflect-metadata": "^0.2.2",
"server-only": "^0.0.1",
"tailwind-merge": "^2.5.4",
diff --git a/src/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm.ts b/src/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm.ts
index 50c4620..5e20d21 100644
--- a/src/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm.ts
+++ b/src/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm.ts
@@ -15,7 +15,7 @@ type LinkItem = {
* so they come together always and there is no need to be connected with interface for reusable
* vms.
*/
-export default function navLinkPersonalVM() {
+export default function useNavLinkPersonalVM() {
const pathname = usePathname();
// Map of links to display in the side navigation.
// Depending on the size of the application, this would be stored in a database.
diff --git a/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
index 8c2f587..f5ca830 100644
--- a/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
+++ b/src/app/[lang]/dashboard/components/client/nav-links/nav-links.tsx
@@ -1,11 +1,11 @@
"use client";
-import navLinkPersonalVM from "@/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm";
+import useNavLinkPersonalVM from "@/app/[lang]/dashboard/components/client/nav-links/nav-link.personal-vm";
import clsx from "clsx";
import Link from "next/link";
export default function NavLinks() {
- const { links, isLinkActive } = navLinkPersonalVM();
+ const { links, isLinkActive } = useNavLinkPersonalVM();
return (
<>
{links.map((link) => {
diff --git a/src/app/[lang]/dashboard/layout.tsx b/src/app/[lang]/dashboard/layout.tsx
index 41a0efc..f4851a1 100644
--- a/src/app/[lang]/dashboard/layout.tsx
+++ b/src/app/[lang]/dashboard/layout.tsx
@@ -2,13 +2,13 @@
import SideNav from "@/app/[lang]/dashboard/components/server/sidenav";
import dashboardAppModule from "@/app/[lang]/dashboard/module/dashboard.app-module";
-import { DiContext } from "@/bootstrap/di/di-context";
import { useRef } from "react";
+import { ReactVVMDiProvider } from "reactvvm";
export default function Layout({ children }: { children: React.ReactNode }) {
const di = useRef(dashboardAppModule());
return (
-
+
@@ -17,6 +17,6 @@ export default function Layout({ children }: { children: React.ReactNode }) {
{children}
-
+
);
}
diff --git a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
index ccd0622..235e305 100644
--- a/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/[lang]/dashboard/vm/create-random-invoice-button-vm.ts
@@ -4,12 +4,12 @@ import createInvoiceController from "@/app/[lang]/dashboard/controller/create-in
import ButtonVm from "@/app/components/button/button.i-vm";
import { useServerAction } from "@/bootstrap/helpers/hooks/use-server-action";
import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
-import BaseVM from "@/bootstrap/helpers/vm/base-vm";
import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice.param";
import { faker } from "@faker-js/faker";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";
+import { BaseVM } from "reactvvm";
/**
* Viewmodel for the button view to connect to business logics and all UI logics
diff --git a/src/app/components/button/button.tsx b/src/app/components/button/button.tsx
index 4cf3a5b..8e3da4c 100644
--- a/src/app/components/button/button.tsx
+++ b/src/app/components/button/button.tsx
@@ -1,12 +1,12 @@
"use client";
-import BaseView, { BuildProps } from "@/bootstrap/helpers/view/base-view";
import ButtonVm from "@/app/components/button/button.i-vm";
import { ReactNode } from "react";
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/bootstrap/helpers/lib/ui-utils";
+import { BaseView, BuildProps } from "reactvvm";
export default class Button extends BaseView {
protected Build(props: BuildProps): ReactNode {
diff --git a/src/bootstrap/di/di-context.tsx b/src/bootstrap/di/di-context.tsx
deleted file mode 100644
index 3505eff..0000000
--- a/src/bootstrap/di/di-context.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-"use client";
-
-import di from "@/bootstrap/di/init-di";
-import { createContext, use } from "react";
-import { DependencyContainer } from "tsyringe";
-
-const DiContext = createContext(di);
-
-const useDI = () => {
- const di = use(DiContext);
-
- if (!di) {
- throw new Error("Di has not provided");
- }
-
- return di;
-};
-
-export { DiContext, useDI };
diff --git a/src/bootstrap/helpers/view/base-view.tsx b/src/bootstrap/helpers/view/base-view.tsx
deleted file mode 100644
index e68a0fe..0000000
--- a/src/bootstrap/helpers/view/base-view.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-/* eslint-disable react-hooks/rules-of-hooks */
-/* eslint-disable react/display-name */
-/* eslint-disable @typescript-eslint/no-explicit-any */
-/* eslint-disable react/jsx-props-no-spreading */
-
-"use client";
-
-import { useDI } from "@/bootstrap/di/di-context";
-import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
-import { Component, ReactNode, FC, PropsWithChildren, memo } from "react";
-import { InjectionToken } from "tsyringe";
-
-/* -------------------------------------------------------------------------- */
-/* Connector Component */
-/* -------------------------------------------------------------------------- */
-interface IVvmConnector extends PropsWithChildren {
- View: FC;
- Vm: IBaseVM;
- restProps?: PROPS;
- memoizedByVM?: boolean;
-}
-
-/**
- * This function is just will be used in
- */
-const VvmConnector = memo(
- (props: IVvmConnector) => {
- const { View, Vm, restProps, children } = props;
- const vm = Vm.useVM();
-
- const allProps = {
- restProps,
- vm,
- };
-
- return {children};
- },
- (prevProps) => {
- if (prevProps.memoizedByVM) return true;
- return false;
- },
-);
-
-/* -------------------------------------------------------------------------- */
-/* BaseView */
-/* -------------------------------------------------------------------------- */
-type IVMParent = Record;
-type IPropParent = Record | undefined;
-
-type BaseProps = {
- restProps?: PROPS;
- /**
- * By default it's true.
- * If you pass true this view will update just by changes of vm not rest props
- *
- */
- memoizedByVM?: boolean;
- children?: ReactNode;
-};
-
-type BasePropsWithVM<
- IVM extends IVMParent,
- PROPS extends IPropParent = undefined,
-> = BaseProps & {
- /**
- * Directly instantiated vm
- */
- vm: IBaseVM;
-};
-
-type BasePropsWithVMKey =
- BaseProps & {
- /**
- * TSyringe key for vm to be injected
- */
- vmKey: InjectionToken;
- };
-
-export type BuildProps<
- IVM extends IVMParent,
- PROPS extends IPropParent = undefined,
-> = {
- vm: IVM;
- restProps: PROPS;
- children?: ReactNode;
-};
-
-export type ViewProps<
- IVM extends IVMParent,
- PROPS extends IPropParent = undefined,
-> = BasePropsWithVM | BasePropsWithVMKey;
-
-/**
- * Base view is base component for all views in mvvm architecture which gets
- * vm as props and connect it to the view and memoize the component by default
- * to just render just on changes of its vm.
- */
-export default abstract class BaseView<
- IVM extends IVMParent,
- PROPS extends IPropParent = undefined,
-> extends Component> {
- private vm: IBaseVM | undefined;
-
- constructor(props: ViewProps) {
- super(props);
- this.vm = this.initVm;
- }
-
- private get initVm() {
- if (Object.hasOwn(this.props, "vmKey")) {
- const { vmKey } = this.props as BasePropsWithVMKey;
- const di = useDI();
- return di.resolve(vmKey) as IBaseVM;
- }
- return (this.props as BasePropsWithVM).vm;
- }
-
- protected get componentName() {
- return this.constructor.name;
- }
-
- protected abstract Build(props: BuildProps): ReactNode;
-
- render(): ReactNode {
- const { restProps, memoizedByVM, children, ...rest } = this.props;
- VvmConnector.displayName = this.componentName;
- const vm = memoizedByVM ? this.vm : this.initVm;
- if (!vm) {
- const isVmKey = Object.hasOwn(this.props, "vmKey");
- const message = isVmKey
- ? "vm is not defined, check your di configuration"
- : "pass correct vm";
- throw new Error(`Vm is not defined${message}`);
- }
-
- return (
-
- {children}
-
- );
- }
-}
diff --git a/src/bootstrap/helpers/view/storybook-with-arg-vm.ts b/src/bootstrap/helpers/view/storybook-with-arg-vm.ts
index 7a3387c..583c457 100644
--- a/src/bootstrap/helpers/view/storybook-with-arg-vm.ts
+++ b/src/bootstrap/helpers/view/storybook-with-arg-vm.ts
@@ -1,4 +1,4 @@
-import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
+import { IBaseVM } from "reactvvm";
/**
* To use with mvvm library to make a vm based on props so you can pass the result to the view
diff --git a/src/bootstrap/helpers/vm/base-vm.ts b/src/bootstrap/helpers/vm/base-vm.ts
deleted file mode 100644
index d3b02c4..0000000
--- a/src/bootstrap/helpers/vm/base-vm.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-"use client";
-
-import { useDI } from "@/bootstrap/di/di-context";
-import { NoOverride } from "@/bootstrap/helpers/type-helper";
-import IBaseVM from "@/bootstrap/helpers/vm/i-base-vm";
-import { useState } from "react";
-
-/**
- * Base class for all viewmodels. It provides
- * - dependency injection: To get closes di which serves from di provider
- * - rerender method: to rerender your component manually
- * - produce method: to produce your vm dynamically by passing and attaching dependencies to it
- */
-export default abstract class BaseVM<
- IVM,
- DEP extends object | undefined = undefined,
-> implements IBaseVM
-{
- /* ------------------------------ Dependencies ------------------------------ */
- protected deps!: DEP;
-
- /* -------------------------------- Abstracts ------------------------------- */
- abstract useVM(): IVM;
-
- /* -------------------------------------------------------------------------- */
- produce(dep?: DEP) {
- if (dep) this.deps = dep;
-
- return this;
- }
-
- /* --------------------------------- Getters -------------------------------- */
- /**
- * You can pass your rerender method after calling useRerender on your vm
- * so you can access to it in any method
- */
- protected rerender?: () => void;
-
- /* -------------------------------------------------------------------------- */
- protected get di() {
- return useDI();
- }
-
- /* -------------------------------------------------------------------------- */
- /**
- * You can use this hook in your useVm method to get rerender method
- * @returns Rerender Method that when ever you call it you can rerender your component
- * for showing new values
- */
- protected useRerender(): NoOverride<() => void> {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_, reState] = useState(false);
-
- const rerender = () => reState((prev) => !prev);
- return rerender as NoOverride<() => void>;
- }
-
- /* -------------------------------------------------------------------------- */
-}
diff --git a/src/bootstrap/helpers/vm/i-base-vm.ts b/src/bootstrap/helpers/vm/i-base-vm.ts
deleted file mode 100644
index e2530bd..0000000
--- a/src/bootstrap/helpers/vm/i-base-vm.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * All viewmodels should implement this interface.
- */
-export default interface IBaseVM {
- useVM(): VM;
-}
diff --git a/yarn.lock b/yarn.lock
index 413bcc0..16dae25 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6493,6 +6493,11 @@ react@19.0.0-rc-69d4b800-20241021:
dependencies:
loose-envify "^1.1.0"
+reactvvm@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/reactvvm/-/reactvvm-1.1.0.tgz#2cdb7ebb75ec0fed4121960077c13474536d18e1"
+ integrity sha512-HBb/9SN5gAFhYGaQ5qzxmT+YZ0ML+7hfFfAH0milq8T+hJWY9seUsHUUvYxROuxg1fY+WIR04ENGB1H4HhAYJg==
+
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"