فهرست منبع

[frontend]Pathbrowser in file chooser to view file path (#3130)

Nidhi Bhat G 2 سال پیش
والد
کامیت
2c65c4ff6b

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 15
desktop/core/src/desktop/js/components/icons/HdfsIcon.tsx


+ 17 - 18
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserModal/FileChooserModal.scss

@@ -16,28 +16,27 @@
 
 @import '../../../components/styles/colors';
 
-.file-chooser__modal .ant-modal-body {
+.hue-file-chooser__modal .ant-modal-body {
   padding: 0;
   height: 554px;
-}
 
-.file-system__panel.ant-menu {
-  height: 554px;
-  width: 187px;
-  background-color: $fluid-gray-100;
-  padding-top: 20px;
-}
+  .hue-file-system__panel {
+    height: 554px;
+    background-color: $fluid-gray-100;
+    padding-top: 20px;
 
-.file-system__panel .ant-menu-item {
-  display: flex !important;
-  align-items: center;
-}
+    .ant-menu-item {
+      display: flex;
+      align-items: center;
+    }
 
-.file-system__panel .ant-menu-item-icon {
-  height: 20px !important;
-  width: 20px !important;
-}
+    .ant-menu-item-icon {
+      height: 20px;
+      width: 20px;
+    }
 
-.file-system__panel .ant-menu-item-selected {
-  background-color: $fluid-gray-300 !important;
+    .ant-menu-item-selected {
+      background-color: $fluid-gray-300;
+    }
+  }
 }

+ 47 - 9
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserModal/FileChooserModal.tsx

@@ -16,15 +16,17 @@
 
 import React, { useState, useEffect } from 'react';
 import Modal from 'antd/lib/modal/Modal';
-import { Menu, Spin } from 'antd';
+import { Col, Menu, Row, Spin } from 'antd';
 
 import HdfsIcon from '../../../components/icons/HdfsIcon';
 import S3Icon from '../../../components/icons/S3Icon';
 import AdlsIcon from '../../../components/icons/AdlsIcon';
 
-import { fetchFileSystems } from '../api';
-import { FileSystem } from '../types';
+import { fetchFileSystems, fetchFiles } from '../api';
+import { FileSystem, PathAndFileData } from '../types';
 import './FileChooserModal.scss';
+import PathBrowser from '../PathBrowser/PathBrowser';
+
 interface FileProps {
   show: boolean;
   onCancel: () => void;
@@ -36,7 +38,10 @@ const defaultProps = { title: 'Choose a file', okText: 'Select' };
 
 const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }) => {
   const [fileSystemList, setFileSystemList] = useState<FileSystem[] | undefined>();
+  const [filePath, setFilePath] = useState<string | undefined>();
+  const [filesData, setFilesData] = useState<PathAndFileData | undefined>();
   const [loading, setLoading] = useState(true);
+  const [loadingFiles, setloadingFiles] = useState(true);
 
   const icons = {
     hdfs: <HdfsIcon />,
@@ -53,10 +58,6 @@ const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }
     onCancel();
   };
 
-  const onPathChange = e => {
-    //TODO: Use fileSystemList[e.key].user_home_dir to retrieve files
-  };
-
   useEffect(() => {
     if (show && !fileSystemList) {
       setLoading(true);
@@ -71,6 +72,9 @@ const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }
             };
           });
           setFileSystemList(fileSystemsObj);
+          if (fileSystems.length !== 0) {
+            setFilePath(fileSystems[0].user_home_directory);
+          }
         })
         .catch(error => {
           //TODO: Properly handle errors.
@@ -81,6 +85,22 @@ const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }
     }
   }, [show]);
 
+  useEffect(() => {
+    if (filePath) {
+      setloadingFiles(true);
+      fetchFiles(filePath)
+        .then(responseFilesData => {
+          setFilesData(responseFilesData);
+        })
+        .catch(error => {
+          //TODO: handle errors
+        })
+        .finally(() => {
+          setloadingFiles(false);
+        });
+    }
+  }, [filePath]);
+
   return (
     <Modal
       title={title}
@@ -89,10 +109,28 @@ const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }
       onCancel={handleCancel}
       okText={okText}
       width={930}
-      className="file-chooser__modal"
+      className="hue-file-chooser__modal"
     >
       <Spin spinning={loading}>
-        <Menu items={fileSystemList} onSelect={onPathChange} className="file-system__panel"></Menu>
+        <Row>
+          <Col span={5}>
+            <Menu
+              items={fileSystemList}
+              onSelect={selectedMenuItem => {
+                setFilePath(fileSystemList[selectedMenuItem.key].user_home_dir);
+              }}
+              className="hue-file-system__panel"
+            ></Menu>
+          </Col>
+          <Col span={19}>
+            <Spin spinning={loadingFiles}>
+              <PathBrowser
+                handleFilePathChange={setFilePath}
+                breadcrumbs={filesData?.breadcrumbs}
+              ></PathBrowser>
+            </Spin>
+          </Col>
+        </Row>
       </Spin>
     </Modal>
   );

+ 1 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserWithButton/FileChooserWithButton.scss

@@ -16,7 +16,7 @@
 
 @import '../../../components/styles/colors';
 
-.file-chooser__button {
+.hue-file-chooser__button {
   background-color: $hue-primary-color-dark;
   color: white;
   margin-left: 40px;

+ 1 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserWithButton/FileChooserWithButton.tsx

@@ -31,7 +31,7 @@ const FileChooserWithButton: React.FC<FileChooserWithButtonProps> = ({ title }):
 
   return (
     <>
-      <Button className="file-chooser__button" type="primary" onClick={() => setShow(true)}>
+      <Button className="hue-file-chooser__button" type="primary" onClick={() => setShow(true)}>
         {title}
       </Button>
 

+ 23 - 11
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserWithButton/FileChooserWithButton.test.tsx → desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowTooltip.tsx

@@ -14,17 +14,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { render, screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import '@testing-library/jest-dom';
+import React, { ReactElement, ReactNode } from 'react';
+import { Tooltip } from 'antd';
 
-import React from 'react';
+interface OverflowTooltipProps {
+  title: string;
+  isOverflowing: boolean;
+  toolTipTriggers: string | string[];
+  children: ReactNode | ReactElement;
+}
 
-import FileChooserWithButton from './FileChooserWithButton';
+const OverflowTooltip: React.FC<OverflowTooltipProps> = ({
+  title,
+  isOverflowing,
+  toolTipTriggers,
+  children
+}) => {
+  return isOverflowing ? (
+    <Tooltip title={title} trigger={toolTipTriggers}>
+      {children}
+    </Tooltip>
+  ) : (
+    <>{children}</>
+  );
+};
 
-test('Filechooser modal opens on button click', async () => {
-  const user = userEvent.setup();
-  const { queryByText } = render(<FileChooserWithButton title={'File chooser component'} />);
-  await user.click(screen.getByRole('button', { name: 'File chooser component' }));
-  expect(queryByText('Choose a file')).toBeInTheDocument();
-});
+export default OverflowTooltip;

+ 30 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.scss

@@ -0,0 +1,30 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+.hue-path-browser__overflowing-label {
+  background-color: transparent;
+  white-space: nowrap;
+  padding: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: none;
+  flex: 0 1 auto;
+  box-shadow: none;
+
+  > span {
+    display: contents;
+  }
+}

+ 86 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.tsx

@@ -0,0 +1,86 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React, { useEffect, useRef, useState } from 'react';
+import { Button } from 'antd';
+
+import OverflowTooltip from './OverflowTooltip';
+import './OverflowingItem.scss';
+
+type OverflowingComponentType = 'breadcrumb' | 'menu';
+interface OverflowingItemProps {
+  label: string;
+  url: string;
+  handleFilePathChange: (path: string) => void;
+  componentType: OverflowingComponentType;
+}
+
+//TODO: change to add classnames to components instead of inline styles
+const customStyles: {
+  [key in OverflowingComponentType]: React.CSSProperties;
+} = {
+  breadcrumb: {
+    minWidth: '5ch'
+  },
+  menu: {
+    width: '100%',
+    textAlign: 'left'
+  }
+};
+
+const OverflowingItem: React.FC<OverflowingItemProps> = ({
+  label,
+  url,
+  handleFilePathChange,
+  componentType
+}) => {
+  const textElementRef = useRef<HTMLDivElement>(null);
+  const [isOverflown, setIsOverflown] = useState(false);
+
+  const compareSize = () => {
+    const element = textElementRef.current;
+
+    const compare = element
+      ? element.offsetWidth < element.scrollWidth || element.offsetHeight < element.scrollHeight
+      : false;
+    setIsOverflown(compare);
+  };
+
+  useEffect(() => {
+    compareSize();
+    window.addEventListener('resize', compareSize);
+    return () => {
+      window.removeEventListener('resize', compareSize);
+    };
+  }, []);
+
+  return (
+    <OverflowTooltip isOverflowing={isOverflown} title={label} toolTipTriggers={['hover', 'focus']}>
+      <Button
+        ref={textElementRef}
+        className="hue-path-browser__overflowing-label"
+        onClick={() => {
+          handleFilePathChange(url);
+        }}
+        style={customStyles[componentType]}
+      >
+        {label}
+      </Button>
+    </OverflowTooltip>
+  );
+};
+
+export default OverflowingItem;

+ 123 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.scss

@@ -0,0 +1,123 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import '../../../components/styles/colors';
+
+.hue-path-browser {
+  display: flex;
+
+  .hue-filesystem__icon {
+    object-fit: cover;
+    width: 24px;
+    height: 24px;
+    margin-left: 10px;
+    margin-right: 10px;
+  }
+
+  .hue-toggle-breadcrumb-input:empty {
+    pointer-events: auto;
+    width: 60px;
+    visibility: visible;
+    border: none;
+    box-shadow: none;
+    padding: 0;
+
+    &:hover {
+      cursor: text;
+      background-color: $fluid-gray-100;
+    }
+
+    &:focus {
+      background-color: $fluid-gray-100;
+    }
+  }
+}
+
+.hue-path-browser__breadcrumb {
+  /* stylelint-disable no-invalid-position-at-import-rule */
+  @import './OverflowingItem.scss';
+
+  display: flex;
+  gap: 10px;
+  max-width: 60%;
+
+  &:hover {
+    cursor: pointer;
+  }
+
+  .hue-path-browser__dropdown-button {
+    border: none;
+    box-shadow: none;
+
+    &:hover {
+      background-color: $fluid-gray-100;
+    }
+
+    &:focus {
+      background-color: $fluid-gray-100;
+    }
+  }
+}
+
+.hue-path-browser__breadcrumb-seperator {
+  width: 14px;
+  height: 28px;
+  flex: 0 0 auto;
+}
+
+.hue-path-browser__dropdown {
+  width: 200px;
+
+  .hue-path-browser__dropdown {
+    background-color: $fluid-gray-200;
+    overflow-y: scroll;
+    max-height: 200px;
+  }
+
+  .ant-dropdown-menu-title-content {
+    display: inline-block;
+    overflow-x: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+}
+
+.hue-path-browser-panel {
+  .hue-path-browser__input {
+    width: 300px;
+
+    .ant-input-prefix {
+      width: 24px;
+      height: 24px;
+    }
+  }
+
+  .hue-path-browser-panel__button {
+    border: none;
+    box-shadow: none;
+    color: $fluid-blue-400;
+    padding: 0;
+    width: 100%;
+
+    &:hover {
+      border: $fluid-gray-300 solid 1px;
+    }
+
+    &:focus {
+      border: $fluid-gray-300 solid 1px;
+    }
+  }
+}

+ 195 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.tsx

@@ -0,0 +1,195 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React, { useRef, useEffect, useState, RefObject } from 'react';
+import { Input, Button, Dropdown, Row, Col } from 'antd';
+import type { MenuProps } from 'antd';
+import { RightOutlined } from '@ant-design/icons';
+
+import HdfsIcon from '../../../components/icons/HdfsIcon';
+import S3Icon from '../../../components/icons/S3Icon';
+import AdlsIcon from '../../../components/icons/AdlsIcon';
+
+import { BreadcrumbData } from '../types';
+import OverflowingItem from './OverflowingItem';
+import './PathBrowser.scss';
+
+interface PathBrowserProps {
+  breadcrumbs?: BreadcrumbData[];
+  handleFilePathChange: (path: string) => void;
+}
+
+const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathChange }) => {
+  const [isEditMode, setIsEditMode] = useState(false);
+
+  const icons = {
+    //hdfs file system begins with the first breadcrumb as "/" (ex: /user/demo)
+    '/': <HdfsIcon />,
+    abfs: <AdlsIcon />,
+    s3: <S3Icon />
+  };
+
+  const useOutsideAlerter = (ref: RefObject<HTMLDivElement>) => {
+    useEffect(() => {
+      // Alert if clicked on outside of element
+      const handleClickOutside = (event: MouseEvent) => {
+        const current = ref?.current;
+        if (current && !current.contains(event.target as Node)) {
+          setIsEditMode(false);
+        }
+      };
+      // Bind the event listener
+      document.addEventListener('mousedown', handleClickOutside);
+      return () => {
+        // Unbind the event listener on clean up
+        document.removeEventListener('mousedown', handleClickOutside);
+      };
+    }, []);
+  };
+
+  const extractFileSystem = (label: string) => {
+    const fileSystemPrefix = label.substring(0, label.length - 3);
+    //hdfs file system begins with the first breadcrumb as "/" (ex: /user/demo)
+    if (fileSystemPrefix == '') {
+      return label;
+    } else {
+      return fileSystemPrefix;
+    }
+  };
+
+  const wrapperRef = useRef<HTMLDivElement>(null);
+  useOutsideAlerter(wrapperRef);
+
+  const extractMenuItems = (breadcrumbMenu: BreadcrumbData[]) => {
+    const menu: MenuProps['items'] = breadcrumbMenu.map(breadcrumb => {
+      return {
+        key: breadcrumb.url,
+        label: (
+          <OverflowingItem
+            label={breadcrumb.label}
+            url={breadcrumb.url}
+            handleFilePathChange={handleFilePathChange}
+            componentType="menu"
+          />
+        )
+      };
+    });
+    return menu;
+  };
+
+  if (breadcrumbs) {
+    return (
+      <Row className="hue-path-browser-panel" onClick={e => e.stopPropagation()}>
+        <Col span={18}>
+          {!isEditMode ? (
+            <div className="hue-path-browser">
+              <div className="hue-filesystem__icon">
+                {icons[extractFileSystem(breadcrumbs[0].label)]}
+              </div>
+              <div className="hue-path-browser__breadcrumb">
+                {breadcrumbs.length <= 3 ? (
+                  breadcrumbs.map((item: BreadcrumbData, index: number) => {
+                    return (
+                      <>
+                        <OverflowingItem
+                          key={item.url}
+                          label={index === 0 ? extractFileSystem(item.label) : item.label}
+                          url={item.url}
+                          handleFilePathChange={handleFilePathChange}
+                          componentType="breadcrumb"
+                        />
+                        {index != breadcrumbs.length - 1 ? (
+                          <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
+                        ) : (
+                          <></>
+                        )}
+                      </>
+                    );
+                  })
+                ) : (
+                  <>
+                    <OverflowingItem
+                      label={extractFileSystem(breadcrumbs[0].label)}
+                      url={breadcrumbs[0].url}
+                      handleFilePathChange={handleFilePathChange}
+                      componentType="breadcrumb"
+                      key={breadcrumbs[0].url}
+                    />
+                    <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
+                    <Dropdown
+                      overlayClassName="hue-path-browser__dropdown"
+                      menu={{
+                        items: extractMenuItems(breadcrumbs.slice(1, breadcrumbs.length - 2)),
+                        className: 'hue-path-browser__dropdown-menu'
+                      }}
+                      trigger={['hover', 'click']}
+                      autoFocus
+                    >
+                      <Button className="hue-path-browser__dropdown-button">..</Button>
+                    </Dropdown>
+                    <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
+                    <OverflowingItem
+                      key={breadcrumbs[breadcrumbs.length - 2].url}
+                      label={breadcrumbs[breadcrumbs.length - 2].label}
+                      url={breadcrumbs[breadcrumbs.length - 2].url}
+                      handleFilePathChange={handleFilePathChange}
+                      componentType="breadcrumb"
+                    />
+                    <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
+                    <OverflowingItem
+                      key={breadcrumbs[breadcrumbs.length - 1].url}
+                      label={breadcrumbs[breadcrumbs.length - 1].label}
+                      url={breadcrumbs[breadcrumbs.length - 1].url}
+                      handleFilePathChange={handleFilePathChange}
+                      componentType="breadcrumb"
+                    />
+                  </>
+                )}
+              </div>
+              <Button
+                className="hue-toggle-breadcrumb-input"
+                title="Edit path"
+                onClick={() => {
+                  setIsEditMode(true);
+                }}
+              ></Button>
+            </div>
+          ) : (
+            <div ref={wrapperRef}>
+              <Input
+                prefix={icons[extractFileSystem(breadcrumbs[0].label)]}
+                defaultValue={decodeURIComponent(breadcrumbs[breadcrumbs.length - 1].url)}
+                onPressEnter={customPath => {
+                  handleFilePathChange((customPath.target as HTMLInputElement).value);
+                }}
+                className="hue-path-browser__input"
+                autoFocus
+              ></Input>
+            </div>
+          )}
+        </Col>
+        <Col span={3}>
+          <Button className="hue-path-browser-panel__button">New Folder</Button>
+        </Col>
+        <Col span={3}>
+          <Button className="hue-path-browser-panel__button">Upload</Button>
+        </Col>
+      </Row>
+    );
+  }
+};
+
+export default PathBrowser;

+ 5 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/api.ts

@@ -16,12 +16,16 @@
 
 import { get } from '../../api/utils';
 import { CancellablePromise } from '../../api/cancellablePromise';
+import { PathAndFileData } from './types';
 
 const FILESYSTEMS_API_URL = '/api/storage/filesystems';
-
+const VIEWFILES_API_URl = '/api/storage/view=';
 export interface ApiFileSystem {
   file_system: string;
   user_home_directory: string;
 }
 
 export const fetchFileSystems = (): CancellablePromise<ApiFileSystem[]> => get(FILESYSTEMS_API_URL);
+
+export const fetchFiles = (filePath: string): CancellablePromise<PathAndFileData> =>
+  get(VIEWFILES_API_URl + filePath);

+ 33 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/types.ts

@@ -21,3 +21,36 @@ export interface FileSystem {
   icon: JSX.Element;
   user_home_dir: string;
 }
+
+interface Stats {
+  aclBit: boolean;
+  atime: string;
+  group: string;
+  mode: number;
+  mtime: string;
+  path: string;
+  size: number;
+  user: string;
+}
+
+interface File {
+  humansize: string;
+  is_sentry_managed: boolean;
+  mode: string;
+  mtime: string;
+  name: string;
+  path: string;
+  rwx: string;
+  stats: Stats;
+  type: string;
+  url: string;
+}
+export interface BreadcrumbData {
+  label: string;
+  url: string;
+}
+export interface PathAndFileData {
+  path: string;
+  breadcrumbs: BreadcrumbData[];
+  files: File[];
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


+ 9 - 0
desktop/core/src/desktop/static/desktop/less/root-wrapped-antd.less

@@ -24,6 +24,12 @@
   @import 'node_modules/antd/es/segmented/style/index.less';
   @import 'node_modules/antd/es/select/style/index.less';
   @import 'node_modules/antd/es/divider/style/index.less';
+  @import 'node_modules/antd/es/breadcrumb/style/index.less';
+  @import 'node_modules/antd/es/input/style/index.less';
+  @import 'node_modules/antd/es/dropdown/style/index.less';
+  @import 'node_modules/antd/es/tooltip/style/index.less';
+  @import 'node_modules/antd/es/grid/style/index.less';
+
   /* stylelint-enable no-invalid-position-at-import-rule */
 
   // Required global overrides:
@@ -45,3 +51,6 @@ body.ant-scrolling-effect {
 /* stylelint-disable no-invalid-position-at-import-rule */
 @import 'antd/es/menu/style/index.less';
 @import 'antd/es/style/core/motion.less';
+@import 'antd/es/dropdown/style/index.less';
+@import 'antd/es/tooltip/style/index.less';
+@import 'antd/es/spin/style/index.less';

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 303 - 218
package-lock.json


+ 2 - 1
package.json

@@ -30,9 +30,10 @@
     "node": ">=0.10.0"
   },
   "dependencies": {
+    "@ant-design/icons": "5.0.1",
     "@gethue/sql-formatter": "4.0.3",
     "@selectize/selectize": "0.14.0",
-    "antd": "4.23.1",
+    "antd": "4.24.5",
     "axios": "0.24.0",
     "babel-preset-react-app": "3.1.2",
     "classnames": "2.3.2",

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است