Yuki Takei 2 месяцев назад
Родитель
Сommit
4cc55a9cd6

+ 6 - 3
apps/app/src/client/components/Admin/Users/GrantAdminButton.tsx

@@ -7,11 +7,14 @@ import { toastError, toastSuccess } from '~/client/util/toastr';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
-type GrantAdminButtonProps = {
-  adminUsersContainer: AdminUsersContainer;
+type GrantAdminButtonExternalProps = {
   user: IUserHasId;
 };
 
+type GrantAdminButtonProps = GrantAdminButtonExternalProps & {
+  adminUsersContainer: AdminUsersContainer;
+};
+
 const GrantAdminButton = (props: GrantAdminButtonProps): JSX.Element => {
   const { t } = useTranslation('admin');
   const { adminUsersContainer, user } = props;
@@ -41,7 +44,7 @@ const GrantAdminButton = (props: GrantAdminButtonProps): JSX.Element => {
  * Wrapper component for using unstated
  */
 const GrantAdminButtonWrapper = withUnstatedContainers<
-  unknown,
+  GrantAdminButtonExternalProps,
   GrantAdminButtonProps
 >(GrantAdminButton, [AdminUsersContainer]);
 

+ 9 - 6
apps/app/src/client/components/Admin/Users/UserMenu.tsx

@@ -18,11 +18,14 @@ import UserRemoveButton from './UserRemoveButton';
 
 import styles from './UserMenu.module.scss';
 
-type UserMenuProps = {
-  adminUsersContainer: AdminUsersContainer;
+type UserMenuExternalProps = {
   user: IUserHasId;
 };
 
+type UserMenuProps = UserMenuExternalProps & {
+  adminUsersContainer: AdminUsersContainer;
+};
+
 const UserMenu = (props: UserMenuProps) => {
   const { t } = useTranslation('admin');
 
@@ -146,9 +149,9 @@ const UserMenu = (props: UserMenuProps) => {
 /**
  * Wrapper component for using unstated
  */
-const UserMenuWrapper = withUnstatedContainers<unknown, UserMenuProps>(
-  UserMenu,
-  [AdminUsersContainer],
-);
+const UserMenuWrapper = withUnstatedContainers<
+  UserMenuExternalProps,
+  UserMenuProps
+>(UserMenu, [AdminUsersContainer]);
 
 export default UserMenuWrapper;

+ 7 - 4
apps/app/src/client/components/Admin/Users/UserTable.tsx

@@ -10,7 +10,9 @@ import { withUnstatedContainers } from '../../UnstatedUtils';
 import { SortIcons } from './SortIcons';
 import UserMenu from './UserMenu';
 
-type UserTableProps = {
+type UserTableExternalProps = Record<string, never>;
+
+type UserTableProps = UserTableExternalProps & {
   adminUsersContainer: AdminUsersContainer;
 };
 
@@ -189,8 +191,9 @@ const UserTable = (props: UserTableProps) => {
   );
 };
 
-const UserTableWrapper = withUnstatedContainers(UserTable, [
-  AdminUsersContainer,
-]);
+const UserTableWrapper = withUnstatedContainers<
+  UserTableExternalProps,
+  UserTableProps
+>(UserTable, [AdminUsersContainer]);
 
 export default UserTableWrapper;

+ 28 - 14
apps/app/src/client/components/UnstatedUtils.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Subscribe } from 'unstated';
+import { type Container, Subscribe } from 'unstated';
 
 /**
  * generate K/V object by specified instances
@@ -41,21 +41,35 @@ function generateAutoNamedProps(instances) {
  *    )}
  *  </Subscribe>
  */
-export function withUnstatedContainers<T, P>(
-  Component,
-  containerClasses,
+export function withUnstatedContainers<
+  ExternalProps extends Record<string, unknown>,
+  InternalProps extends ExternalProps = ExternalProps,
+>(
+  Component: React.ComponentType<InternalProps>,
+  containerClasses: (typeof Container)[],
 ): React.ForwardRefExoticComponent<
-  React.PropsWithoutRef<P> & React.RefAttributes<T>
+  React.PropsWithoutRef<ExternalProps> & React.RefAttributes<unknown>
 > {
-  const unstatedContainer = React.forwardRef<T, P>((props, ref) => (
-    // wrap with <Subscribe></Subscribe>
-    <Subscribe to={containerClasses}>
-      {(...containers) => {
-        const propsForContainers = generateAutoNamedProps(containers);
-        return <Component {...props} {...propsForContainers} ref={ref} />;
-      }}
-    </Subscribe>
-  ));
+  const unstatedContainer = React.forwardRef<unknown, ExternalProps>(
+    (props, ref) => (
+      // wrap with <Subscribe></Subscribe>
+      <Subscribe to={containerClasses}>
+        {(...containers) => {
+          // Container props are dynamically generated based on class names
+          const propsForContainers = generateAutoNamedProps(containers) as Omit<
+            InternalProps,
+            keyof ExternalProps
+          >;
+          const mergedProps = {
+            ...props,
+            ...propsForContainers,
+            ref,
+          } as InternalProps & { ref: typeof ref };
+          return <Component {...mergedProps} />;
+        }}
+      </Subscribe>
+    ),
+  );
   unstatedContainer.displayName = 'unstatedContainer';
   return unstatedContainer;
 }