lodash#pickBy TypeScript Examples

The following examples show how to use lodash#pickBy. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utilities.ts    From react-native-jigsaw with MIT License 7 votes vote down vote up
export function extractBorderAndMarginStyles(
  style: StyleProp<any>,
  additionalBorderStyles?: string[],
  additionalMarginStyles?: string[]
) {
  const flatStyle = StyleSheet.flatten(style || {});

  const borderStyles = pickBy(
    pick(flatStyle, [
      ...borderStyleNames,
      ...(additionalBorderStyles ? additionalBorderStyles : []),
    ]),
    identity
  );

  const marginStyles = pickBy(
    pick(flatStyle, [
      ...marginStyleNames,
      ...(additionalMarginStyles ? additionalMarginStyles : []),
    ]),
    identity
  );

  return { borderStyles, marginStyles };
}
Example #2
Source File: index.ts    From TidGi-Desktop with Mozilla Public License 2.0 6 votes vote down vote up
/**
   * load workspaces in sync, and ensure it is an Object
   */
  getInitWorkspacesForCache = (): Record<string, IWorkspace> => {
    const workspacesFromDisk = settings.getSync(`workspaces`) ?? {};
    return typeof workspacesFromDisk === 'object' && !Array.isArray(workspacesFromDisk)
      ? mapValues(pickBy(workspacesFromDisk, (value) => value !== null) as unknown as Record<string, IWorkspace>, (workspace) =>
          this.sanitizeWorkspace(workspace),
        )
      : {};
  };
Example #3
Source File: find-classes-matching-type.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function findClassesMatchingType({
  type,
  model,
}: {
  type: UI5Class | UI5Interface;
  model: UI5SemanticModel;
}): UI5Class[] {
  const matchingClasses = pickBy(model.classes, (_) => classIsOfType(_, type));
  return values(matchingClasses);
}
Example #4
Source File: index.tsx    From roam-toolkit with MIT License 6 votes vote down vote up
ReactHotkeys = ({keyMap, handlers}: Props) => {
    /**
     * Key sequences like 'g g' mess up the other shortcuts
     * See https://github.com/greena13/react-hotkeys/issues/229
     * And https://github.com/greena13/react-hotkeys/issues/219
     *
     * Workaround by separating sequences and single chords into different react components:
     * https://github.com/greena13/react-hotkeys/issues/219#issuecomment-540680435
     */
    const hotkeys: Dictionary<Hotkey> = mapValues(
        zipObjects(keyMap, handlers),
        ([keySequenceString, handler]): Hotkey => [KeySequence.fromString(keySequenceString), handler]
    )

    const singleChordHotkeys = pickBy(hotkeys, usesOneKeyChord)
    const multiChordHotkeys = pickBy(hotkeys, usesMultipleKeyChords)

    return (
        <>
            <GlobalHotKeysWithoutConflictingWithNativeHotkeys
                keyMap={mapValues(singleChordHotkeys, toKeySequence)}
                handlers={mapValues(singleChordHotkeys, toHandler)}
            />
            <GlobalHotKeysWithoutConflictingWithNativeHotkeys
                keyMap={mapValues(multiChordHotkeys, toKeySequence)}
                handlers={mapValues(multiChordHotkeys, toHandler)}
            />
        </>
    )
}
Example #5
Source File: export-file.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
ExportFile = ({ visible, onCancel, versionID, assetID, specProtocol: curProtocol }: IProps) => {
  const fieldsList: IFormItem[] = React.useMemo(() => {
    const filterProtocolMap = pickBy(protocolMap, (_, protocol) => {
      const protocolPrefix = curProtocol?.substr(0, 4) || '';
      return protocol.indexOf(protocolPrefix) > -1;
    });
    return [
      {
        label: i18n.t('export format'),
        name: 'specProtocol',
        type: 'select',
        options: map(filterProtocolMap, ({ name }, key) => ({ name, value: key })),
      },
    ];
  }, [curProtocol]);
  const handleExport = ({ specProtocol }: { specProtocol: API_MARKET.SpecProtocol }) => {
    window.open(exportSwagger({ assetID, versionID, specProtocol }));
    onCancel();
  };
  return (
    <FormModal
      title={i18n.t('Export')}
      visible={visible}
      fieldsList={fieldsList}
      onOk={handleExport}
      onCancel={onCancel}
    />
  );
}
Example #6
Source File: non-unique-id.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function validateNonUniqueID(xmlDoc: XMLDocument): NonUniqueIDIssue[] {
  const idCollector = new IdsCollectorVisitor();
  accept(xmlDoc, idCollector);
  const idsToXMLElements = idCollector.idsToXMLElements;
  const duplicatedIdsRecords = pickBy(idsToXMLElements, (_) => _.length > 1);

  const allIDsIssues: NonUniqueIDIssue[] = flatMap(
    duplicatedIdsRecords,
    buildIssuesForSingleID
  );

  return allIDsIssues;
}
Example #7
Source File: go-to.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
pathFormat = (url: string) => (params: object) => {
  const necessaryArg: string[] = [];
  const [_path, _query] = url.split('?');
  // query不算必须的路由参数
  const newPath = _path.replace(/\{(\w+)\}/g, (_, key) => {
    necessaryArg.push(key);
    return params[key];
  });
  const lostArg = filter(necessaryArg, (arg: string) => params[arg] === undefined);
  if (lostArg.length) {
    // eslint-disable-next-line no-console
    console.error('Jump missing parameters:', lostArg, `in url: ${url}`);
    return undefined;
  }
  if (!_query) {
    return newPath;
  }
  let newQuery = _query.replace(/\{(\w+)\}/g, (_, key) => {
    return params[key] !== undefined && params[key] !== null ? params[key] : '';
  });
  // 移除空的query参数
  newQuery = qs.stringify(pickBy(qs.parse(newQuery), (v: any) => v !== ''));
  return newQuery ? [newPath, newQuery].join('?') : newPath;
}
Example #8
Source File: MjmlToJson.ts    From easy-email with MIT License 6 votes vote down vote up
export function getMetaDataFromMjml(data?: IChildrenItem): {
  [key: string]: any;
} {
  const mjmlHtmlAttributes = data?.children
    ?.filter((item) => item.tagName === 'mj-html-attributes')
    .map((item) => item.children)
    .flat()
    .filter((item) => item && item.attributes.class === 'easy-email')
    .reduce((obj: { [key: string]: any }, item) => {
      if (!item) return obj;
      const name = item.attributes['attribute-name'];
      const isMultipleAttributes = Boolean(
        item.attributes['multiple-attributes']
      );
      obj[name] = isMultipleAttributes
        ? pickBy(
            {
              ...item.attributes,
              'attribute-name': undefined,
              'multiple-attributes': undefined,
              class: undefined,
            },
            identity
          )
        : item.attributes[name];
      return obj;
    }, {});

  return pickBy(mjmlHtmlAttributes, identity);
}
Example #9
Source File: flows.selectors.ts    From dashboard with Apache License 2.0 6 votes vote down vote up
selectExampleFlowsKeyEntryPairs = (state: State) => {
  const { selectedWorkspaceId } = state.flowState
  const workspaceFlows = pickBy(
    state.flowState.flows,
    (flow) => flow.workspaceId === selectedWorkspaceId
  )
  return Object.entries(workspaceFlows).filter(
    (flowKeyEntryPair) => flowKeyEntryPair[1].type === "example"
  )
}
Example #10
Source File: flows.test.ts    From dashboard with Apache License 2.0 6 votes vote down vote up
describe("flow selectors", () => {
  const state = {
    flowState: testFlowState,
  }

  it("should select selected flow", () => {
    const { selectedFlowId } = testFlowState.workspaces[
      state.flowState.selectedWorkspaceId
    ]

    expect(selectSelectedFlow(state)).toEqual(
      testFlowState.flows[selectedFlowId]
    )
  })

  it("should select example flows key entry pairs", () => {
    const { selectedWorkspaceId } = testFlowState

    const workspaceFlows = pickBy(
      testFlowState.flows,
      (flow) => flow.workspaceId === selectedWorkspaceId
    )

    expect(selectExampleFlowsKeyEntryPairs(state)).toEqual(
      Object.entries(workspaceFlows).filter(
        (flowKeyEntryPair) => flowKeyEntryPair[1].type === "example"
      )
    )
  })

  it("should select selected flowId", () => {
    expect(selectSelectedFlowId(state)).toBe(
      testFlowState.workspaces[testFlowState.selectedWorkspaceId].selectedFlowId
    )
  })
})
Example #11
Source File: bitriseApi.client.ts    From backstage with Apache License 2.0 5 votes vote down vote up
async getBuilds(
    appSlug: string,
    params?: BitriseQueryParams,
  ): Promise<BitriseBuildListResponse> {
    const baseUrl = await this.discoveryApi.getBaseUrl('proxy');
    let url = `${baseUrl}/bitrise/apps/${appSlug}/builds`;

    if (params) {
      url = `${url}?${qs.stringify(pickBy(params, identity))}`;
    }

    const response = await fetch(url);
    const data = await response.json();
    const builds: BitriseBuildResponseItem[] = data.data;

    return {
      data: builds.map(
        (build: BitriseBuildResponseItem): BitriseBuildResult => {
          const duration = String(
            Math.round(
              Interval.fromDateTimes(
                DateTime.fromISO(build.started_on_worker_at),
                DateTime.fromISO(build.finished_at),
              ).length('minutes'),
            ),
          );

          return {
            id: build.build_number,
            source: build.commit_view_url,
            status: build.status,
            statusText: build.status_text,
            buildSlug: build.slug,
            message: `${build.branch}`,
            workflow: build.triggered_workflow,
            commitHash: `${build.commit_hash}`,
            triggerTime: build.triggered_at,
            duration: `${duration} minutes`,
            appSlug,
          };
        },
      ),
      paging: data.paging,
    };
  }
Example #12
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
removeEmptyQuery = (href: string) => {
  const { query, url } = parseUrl(href);
  const queryString = stringify(pickBy(query, (v: any) => v !== ''));
  return `${url}${queryString ? `?${queryString}` : ''}`;
}
Example #13
Source File: flows.selectors.ts    From dashboard with Apache License 2.0 5 votes vote down vote up
selectFlows = (state: State) => {
  const { selectedWorkspaceId } = state.flowState
  return pickBy(
    state.flowState.flows,
    (flow) => flow.workspaceId === selectedWorkspaceId
  )
}
Example #14
Source File: login.ts    From linkedin-private-api with MIT License 5 votes vote down vote up
parseCookies = <T>(cookies: string[]): Partial<T> =>
  cookies.reduce((res, c) => {
    let parsedCookie = parseCookie(c);

    parsedCookie = pickBy(parsedCookie, (v, k) => k === Object.keys(parsedCookie)[0]);

    return merge(res, parsedCookie);
  }, {})
Example #15
Source File: AnnotateScmSlugEntityProcessor.ts    From backstage with Apache License 2.0 5 votes vote down vote up
async preProcessEntity(
    entity: Entity,
    location: LocationSpec,
  ): Promise<Entity> {
    if (entity.kind !== 'Component' || location.type !== 'url') {
      return entity;
    }

    const scmIntegration = this.opts.scmIntegrationRegistry.byUrl(
      location.target,
    );

    if (!scmIntegration) {
      return entity;
    }

    let annotation;
    switch (scmIntegration.type) {
      case 'github':
        annotation = GITHUB_ACTIONS_ANNOTATION;
        break;
      case 'gitlab':
        annotation = GITLAB_ACTIONS_ANNOTATION;
        break;
      default:
        return entity;
    }

    let projectSlug = entity.metadata.annotations?.[annotation];
    if (!projectSlug) {
      const gitUrl = parseGitUrl(location.target);
      projectSlug = `${gitUrl.owner}/${gitUrl.name}`;
    }

    return merge(
      {
        metadata: {
          annotations: pickBy(
            {
              [annotation]: projectSlug,
            },
            identity,
          ),
        },
      },
      entity,
    );
  }
Example #16
Source File: AnnotateLocationEntityProcessor.ts    From backstage with Apache License 2.0 5 votes vote down vote up
async preProcessEntity(
    entity: Entity,
    location: LocationSpec,
    _: CatalogProcessorEmit,
    originLocation: LocationSpec,
  ): Promise<Entity> {
    const { integrations } = this.options;
    let viewUrl;
    let editUrl;
    let sourceLocation;

    if (location.type === 'url') {
      const scmIntegration = integrations.byUrl(location.target);

      viewUrl = location.target;
      editUrl = scmIntegration?.resolveEditUrl(location.target);

      const sourceUrl = scmIntegration?.resolveUrl({
        url: './',
        base: location.target,
      });

      if (sourceUrl) {
        sourceLocation = stringifyLocationRef({
          type: 'url',
          target: sourceUrl,
        });
      }
    }

    return merge(
      {
        metadata: {
          annotations: pickBy(
            {
              [ANNOTATION_LOCATION]: stringifyLocationRef(location),
              [ANNOTATION_ORIGIN_LOCATION]:
                stringifyLocationRef(originLocation),
              [ANNOTATION_VIEW_URL]: viewUrl,
              [ANNOTATION_EDIT_URL]: editUrl,
              [ANNOTATION_SOURCE_LOCATION]: sourceLocation,
            },
            identity,
          ),
        },
      },
      entity,
    );
  }
Example #17
Source File: KeyStores.ts    From backstage with Apache License 2.0 5 votes vote down vote up
/**
   * Looks at the `auth.keyStore` section in the application configuration
   * and returns a KeyStore store. Defaults to `database`
   *
   * @returns a KeyStore store
   */
  static async fromConfig(
    config: Config,
    options?: Options,
  ): Promise<KeyStore> {
    const { logger, database } = options ?? {};

    const ks = config.getOptionalConfig('auth.keyStore');
    const provider = ks?.getOptionalString('provider') ?? 'database';

    logger?.info(`Configuring "${provider}" as KeyStore provider`);

    if (provider === 'database') {
      if (!database) {
        throw new Error('This KeyStore provider requires a database');
      }

      return await DatabaseKeyStore.create({
        database: await database.getClient(),
      });
    }

    if (provider === 'memory') {
      return new MemoryKeyStore();
    }

    if (provider === 'firestore') {
      const settings = ks?.getConfig(provider);

      const keyStore = await FirestoreKeyStore.create(
        pickBy(
          {
            projectId: settings?.getOptionalString('projectId'),
            keyFilename: settings?.getOptionalString('keyFilename'),
            host: settings?.getOptionalString('host'),
            port: settings?.getOptionalNumber('port'),
            ssl: settings?.getOptionalBoolean('ssl'),
            path: settings?.getOptionalString('path'),
            timeout: settings?.getOptionalNumber('timeout'),
          },
          value => value !== undefined,
        ),
      );
      await FirestoreKeyStore.verifyConnection(keyStore, logger);

      return keyStore;
    }

    throw new Error(`Unknown KeyStore provider: ${provider}`);
  }
Example #18
Source File: sumObjectValues.ts    From hub with Apache License 2.0 5 votes vote down vote up
sumObjectValues = (data: { [key: string]: number | undefined }): number => {
  const cleanData = pickBy(data, identity);
  if (isEmpty(cleanData)) return 0;
  return Object.values(cleanData).reduce((a, b) => a! + b!) || 0;
}
Example #19
Source File: removeUndefined.ts    From nextclade with MIT License 5 votes vote down vote up
/**
 * Removes object keys which have `undefined` value
 */
export function removeUndefined<T>(obj: Record<string, T | undefined>) {
  return pickBy(obj, identity) as Record<string, T>
}
Example #20
Source File: base-cell.ts    From S2 with MIT License 5 votes vote down vote up
// 根据当前state来更新cell的样式
  public updateByState(stateName: InteractionStateName, cell: S2CellType) {
    this.spreadsheet.interaction.setInteractedCells(cell);
    const stateStyles = get(
      this.theme,
      `${this.cellType}.cell.interactionState.${stateName}`,
    );

    const { x, y, height, width } = this.getCellArea();

    each(stateStyles, (style, styleKey) => {
      const targetShapeNames = keys(
        pickBy(SHAPE_ATTRS_MAP, (attrs) => includes(attrs, styleKey)),
      );
      targetShapeNames.forEach((shapeName: StateShapeLayer) => {
        const isStateShape = this.stateShapes.has(shapeName);
        const shape = isStateShape
          ? this.stateShapes.get(shapeName)
          : this[shapeName];

        // stateShape 默认 visible 为 false
        if (isStateShape && !shape.get('visible')) {
          shape.set('visible', true);
        }

        // 根据borderWidth更新borderShape大小 https://github.com/antvis/S2/pull/705
        if (
          shapeName === 'interactiveBorderShape' &&
          styleKey === 'borderWidth'
        ) {
          if (isNumber(style)) {
            const marginStyle = {
              x: x + style / 2,
              y: y + style / 2,
              width: width - style - 1,
              height: height - style - 1,
            };
            each(marginStyle, (currentStyle, currentStyleKey) => {
              updateShapeAttr(shape, currentStyleKey, currentStyle);
            });
          }
        }
        updateShapeAttr(shape, SHAPE_STYLE_MAP[styleKey], style);
      });
    });
  }
Example #21
Source File: decorator.ts    From nestjs-paginate with MIT License 5 votes vote down vote up
Paginate = createParamDecorator((_data: unknown, ctx: ExecutionContext): PaginateQuery => {
    const request: Request = ctx.switchToHttp().getRequest()
    const { query } = request

    // Determine if Express or Fastify to rebuild the original url and reduce down to protocol, host and base url
    let originalUrl
    if (request.originalUrl) {
        originalUrl = request.protocol + '://' + request.get('host') + request.originalUrl
    } else {
        originalUrl = request.protocol + '://' + request.hostname + request.url
    }
    const urlParts = new URL(originalUrl)
    const path = urlParts.protocol + '//' + urlParts.host + urlParts.pathname

    const sortBy: [string, string][] = []
    const searchBy: string[] = []

    if (query.sortBy) {
        const params = !Array.isArray(query.sortBy) ? [query.sortBy] : query.sortBy
        for (const param of params) {
            if (isString(param)) {
                const items = param.split(':')
                if (items.length === 2) {
                    sortBy.push(items as [string, string])
                }
            }
        }
    }

    if (query.searchBy) {
        const params = !Array.isArray(query.searchBy) ? [query.searchBy] : query.searchBy
        for (const param of params) {
            if (isString(param)) {
                searchBy.push(param)
            }
        }
    }

    const filter = mapKeys(
        pickBy(
            query,
            (param, name) =>
                name.includes('filter.') &&
                (isString(param) || (Array.isArray(param) && (param as any[]).every((p) => isString(p))))
        ) as Dictionary<string | string[]>,
        (_param, name) => name.replace('filter.', '')
    )

    return {
        page: query.page ? parseInt(query.page.toString(), 10) : undefined,
        limit: query.limit ? parseInt(query.limit.toString(), 10) : undefined,
        sortBy: sortBy.length ? sortBy : undefined,
        search: query.search ? query.search.toString() : undefined,
        searchBy: searchBy.length ? searchBy : undefined,
        filter: Object.keys(filter).length ? filter : undefined,
        path,
    }
})
Example #22
Source File: settings-config-spec.ts    From ui5-language-assistant with Apache License 2.0 5 votes vote down vote up
describe("settings configuration properties", () => {
  let packageJsonSettings: Record<string, Setting>;

  before(() => {
    // Get the settings from the package.json
    const packageJsonPath = require.resolve(
      "vscode-ui5-language-assistant/package.json"
    );
    const packageJsonContent = readJsonSync(packageJsonPath);
    packageJsonSettings =
      packageJsonContent.contributes.configuration.properties;
  });

  it("default setting values in package.json have the correct type", () => {
    forEach(packageJsonSettings, (value, key) => {
      expect(
        typeof value.default,
        `Setting ${key} default value type does not match the setting's defined type`
      ).to.equal(value.type);
    });
  });

  it("settings in package.json are in sync with the settings package", () => {
    const defaultSettingsFromPackageJson = parseSettings(packageJsonSettings);
    const defaultSettings = getDefaultSettings();
    expect(
      defaultSettingsFromPackageJson,
      "settings from package.json don't match the default settings in the language server"
    ).to.deep.equal({
      UI5LanguageAssistant: defaultSettings,
    });
  });

  it("enums in package.json are in sync with the settings package", () => {
    const pkgJsonSettingsWithEnum = pickBy(packageJsonSettings, (_) =>
      has(_, "enum")
    );
    forEach(pkgJsonSettingsWithEnum, (pkgJsonSetting, settingsKey) => {
      const settingsModulePropName = camelCase(
        settingsKey.replace(
          /UI5LanguageAssistant.(\w+)\.(\w+)/,
          "valid $1 $2 values"
        )
      );
      const settingsModulePropValue = keys(
        settingsModule[settingsModulePropName]
      );
      const pkgJsonPropValue = pkgJsonSetting.enum;
      expect(settingsModulePropValue).to.deep.equalInAnyOrder(pkgJsonPropValue);
    });
  });

  it("use the correct logging configuration property name", () => {
    expect(packageJsonSettings[LOGGING_LEVEL_CONFIG_PROP]).to.exist;
    expect(
      packageJsonSettings[LOGGING_LEVEL_CONFIG_PROP].description
    ).to.include("logging");
  });

  type Setting = {
    scope: string;
    type: string;
    default: unknown;
    description: string;
    enum?: string[];
  };

  function parseSettings(properties: Record<string, Setting>): unknown {
    const defaultSettings = {};
    forEach(properties, (value, key) => {
      set(defaultSettings, key, value.default);
    });
    return defaultSettings;
  }
});
Example #23
Source File: BrickTable.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function BrickTable(props: BrickTableProps): React.ReactElement {
  if (props.error) {
    throw props.error;
  }

  const {
    configProps,
    columns,
    rowKey,
    expandIconAsCell,
    expandIconColumnIndex,
    childrenColumnName,
    deleteEnabled,
    scroll,
    optimizedColumns,
    onDelete, // 用于 brick form 中,will be deprecated
    ellipsisInfo,
    showHeader,
  } = props;

  const initData = useMemo(() => {
    return (
      props.dataSource &&
      (rowKey
        ? props.dataSource
        : props.dataSource.map((item, index) =>
            isNil(item.key) ? { ...item, key: index } : item
          ))
    );
  }, [props.dataSource, rowKey]);

  const [data, setData] = useState(initData);
  const rowKeyExpandIconMapRef = useRef<Map<unknown, React.ReactNode>>(
    new Map()
  );
  const columnTitleBrickDataMapRef = useRef<
    Map<CustomColumn, { title: unknown }>
  >(new Map());
  const useBrickItemBrickDataMapRef = useRef<
    Map<UseBrickConf, ItemBrickDataMap>
  >(new Map());
  const itemExpandedRowBrickDataMapRef = useRef<Map<unknown, unknown>>(
    new Map()
  );

  useEffect(() => {
    itemExpandedRowBrickDataMapRef.current.clear();
    setData(initData);
  }, [initData]);

  const expandIconColumnIndexOffset = configProps?.rowSelection ? -1 : 0;
  const customColumns = useMemo(() => {
    if (columns) {
      columnTitleBrickDataMapRef.current.clear();
      useBrickItemBrickDataMapRef.current.clear();
      const customColumns = columns.map((column, index) => {
        const {
          useBrick,
          component,
          valueSuffix,
          cellStatus,
          titleUseBrick,
          headerBrick,
          colSpanKey,
          rowSpanKey,
          ...columnConf
        } = column;
        if (headerBrick?.useBrick || titleUseBrick) {
          if (titleUseBrick) {
            // eslint-disable-next-line no-console
            console.warn(
              "`titleUseBrick` of `<presentational-bricks.brick-table>` is deprecated, use `headerBrick` instead."
            );
          }

          const useBrick = headerBrick?.useBrick || titleUseBrick;
          let data = columnTitleBrickDataMapRef.current.get(column);

          if (!data) {
            data = {
              title: columnConf.title,
            };
            columnTitleBrickDataMapRef.current.set(column, data);
          }

          columnConf.title = getCustomHeader(useBrick, data);
        }

        if (useBrick || component) {
          let itemBrickDataMap: ItemBrickDataMap;

          if (useBrick) {
            itemBrickDataMap =
              useBrickItemBrickDataMapRef.current.get(useBrick);

            if (!itemBrickDataMap) {
              itemBrickDataMap = new Map();
              useBrickItemBrickDataMapRef.current.set(
                useBrick,
                itemBrickDataMap
              );
            }
          }

          columnConf.render = getCustomComp(
            useBrick,
            component,
            itemBrickDataMap
          );
          if (optimizedColumns?.includes(column.dataIndex)) {
            // [only update when record changed](https://ant.design/components/table-cn/#%E4%B8%BA%E4%BB%80%E4%B9%88-%E6%9B%B4%E6%96%B0-state-%E4%BC%9A%E5%AF%BC%E8%87%B4%E5%85%A8%E8%A1%A8%E6%B8%B2%E6%9F%93%EF%BC%9F)
            columnConf.shouldCellUpdate = (record, prevRecord) => {
              return !isEqual(record, prevRecord);
            };
          }
        } else if (valueSuffix) {
          columnConf.render = (value) => value + valueSuffix;
        }
        if (
          !expandIconAsCell &&
          index === expandIconColumnIndex + expandIconColumnIndexOffset
        ) {
          const innerRender = columnConf.render;
          columnConf.render = function ExpandIcon(value, record, index) {
            return (
              <>
                {!record[childrenColumnName] &&
                  rowKeyExpandIconMapRef.current.get(
                    rowKey ? record[rowKey] : record
                  )}
                {innerRender ? innerRender(value, record, index) : value}
              </>
            );
          };
        }
        if (cellStatus || colSpanKey || rowSpanKey) {
          const innerRender = columnConf.render;
          columnConf.render = (value, item, index) => {
            return {
              children: innerRender ? innerRender(value, item, index) : value,
              props: {
                colSpan: item[colSpanKey],
                rowSpan: item[rowSpanKey],
                style: cellStatus && getCellStyle(cellStatus, item, value),
              },
            };
          };
        }

        if (typeof columnConf.dataIndex === "string") {
          columnConf.dataIndex = toPath(columnConf.dataIndex);
        }
        if (columnConf.verticalAlign === "top") {
          columnConf.className
            ? (columnConf.className += " alignTop")
            : (columnConf.className = "alignTop");
        }
        if (columnConf.verticalAlign === "bottom") {
          columnConf.className
            ? (columnConf.className += " alignBottom")
            : (columnConf.className = "alignBottom");
        }
        if (ellipsisInfo) {
          columnConf.className = styles.ellipsisInfoCell;
        }
        return columnConf;
      });

      if (deleteEnabled) {
        const render = (value: any, item: any, index: number) => {
          return (
            <Icon
              onClick={() => onDelete?.(index)}
              component={() => <FontAwesomeIcon icon="trash-alt" />}
              style={{ color: "#167be0" }}
            />
          );
        };

        customColumns.push({
          title: "操作",
          render,
        });
      }

      return customColumns;
    }
  }, [
    columns,
    childrenColumnName,
    expandIconAsCell,
    expandIconColumnIndex,
    expandIconColumnIndexOffset,
    rowKey,
    deleteEnabled,
    onDelete,
    ellipsisInfo,
  ]);

  const expandedRowRender = (record: Record<string, any>, index: number) => {
    let data = itemExpandedRowBrickDataMapRef.current.get(record);

    if (!data) {
      data = {
        rowData: record,
        rowIndex: index,
      };
      itemExpandedRowBrickDataMapRef.current.set(record, data);
    }

    return (
      <BrickAsComponent
        useBrick={props.expandedRowBrick.useBrick}
        data={data}
      />
    );
  };

  const components = {
    body: {
      row: DraggableBodyRow,
    },
  };

  const moveRow = (dragIndex: number, hoverIndex: number) => {
    const dragRow = data[dragIndex];
    const newData = update(data, {
      $splice: [
        [dragIndex, 1],
        [hoverIndex, 0, dragRow],
      ],
    });
    setData(newData);
    props.onDrag && props.onDrag(newData);
  };

  const onExpand = (expanded: boolean, record: Record<string, any>) => {
    props.onExpand && props.onExpand(expanded, record);
  };

  const onExpandedRowsChange = (expandedRows: React.Key[]) => {
    props.onExpandedRowsChange && props.onExpandedRowsChange(expandedRows);
  };

  const getCustomExpandIcon = (iconProps: any) => {
    const { record, expandable, expanded, onExpand } = iconProps;
    let icon = props.expandIcon?.collapsedIcon || downMenuIcon;
    let iconNode: React.ReactNode;
    if (expandable) {
      if (!expanded) {
        icon = props.expandIcon?.expandedIcon || rightMenuIcon;
      }
      iconNode = (
        <span
          className={styles.expandIconSpan}
          data-testid="expand-icon"
          onClick={(e) => {
            onExpand(record, e);
          }}
        >
          <GeneralIcon icon={icon} />
        </span>
      );
    } else {
      iconNode = (
        <span className={styles.expandIconSpan} data-testid="expand-icon">
          <span style={{ visibility: "hidden" }}>
            <GeneralIcon icon={icon} />
          </span>
        </span>
      );
    }

    if (iconNode) {
      if (!expandIconAsCell) {
        rowKeyExpandIconMapRef.current.set(
          rowKey ? record[rowKey] : record,
          iconNode
        );
      }
      return iconNode;
    } else {
      return <></>;
    }
  };

  const pickExpandProps = pickBy(
    {
      expandIconColumnIndex,
      expandIconAsCell,
      expandRowByClick: props.expandRowByClick,
      expandedRowKeys: props.expandedRowKeys,
      defaultExpandAllRows: props.defaultExpandAllRows,
    },
    (item) => !isNil(item)
  );

  let table = (
    <Table
      className={classNames(styles.brickTable, {
        [styles.expandIconCellHidden]: !expandIconAsCell,
        [styles.customDropTable]: props.tableDraggable,
        [styles.tableThTransparent]: props.thTransparent,
        [styles.zebraPatternTable]: data?.length >= 2 && props.zebraPattern,
      })}
      dataSource={data}
      {...(props.tableDraggable
        ? {
            components,
            onRow: (record, index) => ({
              index,
              moveRow: moveRow,
            }),
          }
        : {})}
      columns={customColumns}
      onChange={props.onChange}
      {...(props.expandedRowBrick
        ? {
            expandedRowRender,
          }
        : {})}
      {...pickExpandProps}
      onExpand={onExpand}
      onExpandedRowsChange={onExpandedRowsChange}
      rowKey={rowKey}
      childrenColumnName={childrenColumnName}
      rowClassName={(record, index) =>
        props.zebraPattern && index % 2 ? styles.brickTableOddRow : ""
      }
      expandIcon={getCustomExpandIcon}
      scroll={scroll}
      showHeader={showHeader}
      {...configProps}
    />
  );

  if (props.tableDraggable) {
    table = <DndProvider backend={HTML5Backend}>{table}</DndProvider>;
  }

  if (!props.showCard) {
    return table;
  }

  return <Card bordered={false}> {table} </Card>;
}
Example #24
Source File: flows.reducer.ts    From dashboard with Apache License 2.0 4 votes vote down vote up
flowReducer = produce((draft: FlowState, action: FlowActionTypes) => {
  switch (action.type) {
    case CREATE_NEW_FLOW: {
      draft = _createNewFlow(draft)
      break
    }
    case DUPLICATE_FLOW: {
      draft = _createNewFlow(draft, action.payload)
      break
    }
    case IMPORT_FLOW: {
      draft = _createNewFlow(draft, action.payload)
      break
    }
    case DELETE_FLOW: {
      const flowId = action.payload
      draft.flows = omit(draft.flows, flowId)

      const workspaceId = draft.selectedWorkspaceId

      const { selectedFlowId } = draft.workspaces[workspaceId]

      const workspaceFlows = Object.entries(draft.flows).filter(
        ([id, flow]: [string, Flow]) => {
          return flow.workspaceId === workspaceId
        }
      )

      if (selectedFlowId === flowId && workspaceFlows.length) {
        const firstFlowId = workspaceFlows[0][0]
        draft.workspaces[workspaceId].selectedFlowId = firstFlowId
      } else if (!workspaceFlows.length) {
        const newFlowId = nanoid()
        _createNewFlow(draft, undefined, newFlowId, workspaceId)
        draft.workspaces[workspaceId].selectedFlowId = newFlowId
      }
      break
    }
    case UPDATE_SELECTED_FLOW: {
      const flowUpdate = action.payload
      if (draft.workspaces[draft.selectedWorkspaceId].selectedFlowId) {
        const selectedFlow =
          draft.flows[
            draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
          ]
        draft.flows[
          draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
        ] = {
          ...selectedFlow,
          ...flowUpdate,
        }
      }
      break
    }
    case SET_FLOW_ARGUMENTS: {
      draft.workspaces[draft.selectedWorkspaceId].flowArguments = action.payload
      break
    }
    case LOAD_FLOW:
      draft.workspaces[draft.selectedWorkspaceId].selectedFlowId =
        action.payload
      break
    case UPDATE_NODE: {
      const { nodeUpdate, nodeId } = action.payload
      const selectedFlowId =
        draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
      const oldNodeIndex = draft.flows[
        selectedFlowId
      ].flowChart.elements.findIndex((element) => element.id === nodeId)

      if (oldNodeIndex >= 0) {
        const oldNode =
          draft.flows[selectedFlowId].flowChart.elements[oldNodeIndex]

        const newNode = {
          ...oldNode,
          ...nodeUpdate,
        }

        draft.flows[selectedFlowId].flowChart.elements[oldNodeIndex] = newNode
      }
      break
    }
    case UPDATE_NODE_DATA: {
      const { nodeDataUpdate, nodeId } = action.payload
      const selectedFlowId =
        draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
      const oldNodeIndex = draft.flows[
        selectedFlowId
      ].flowChart.elements.findIndex((element) => element.id === nodeId)

      if (oldNodeIndex >= 0) {
        const oldNode =
          draft.flows[selectedFlowId].flowChart.elements[oldNodeIndex]

        const newData: NodeData = {
          ...oldNode.data,
          ...nodeDataUpdate,
        }

        draft.flows[selectedFlowId].flowChart.elements[
          oldNodeIndex
        ].data = newData
      }

      break
    }
    case ADD_NODE:
      const { data, id, position } = action.payload
      const newNode = createNode(id, data, position)
      draft.flows[
        draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
      ].flowChart.elements.push(newNode)
      break
    case DELETE_NODE:
      const nodeId = action.payload
      const selectedFlow =
        draft.flows[draft.workspaces[draft.selectedWorkspaceId].selectedFlowId]
      const withoutLinksAndNode = selectedFlow.flowChart.elements.filter(
        (element) => {
          if (isFlowNode(element)) return element.id !== nodeId

          if (isFlowEdge(element))
            return element.source !== nodeId && element.target !== nodeId

          return true
        }
      )

      draft.flows[
        draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
      ].flowChart.elements = withoutLinksAndNode
      break
    case ADD_LINK:
      const { source, target } = action.payload
      const newLink = createEdge(source, target)
      draft.flows[
        draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
      ].flowChart.elements.push(newLink)
      break
    case DELETE_LINK:
      if (isNodeConnection(action.payload)) {
        const { source, target } = action.payload as Connection
        draft.flows[
          draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
        ].flowChart.elements = draft.flows[
          draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
        ].flowChart.elements.filter(
          (element: FlowElement) =>
            !(
              isFlowEdge(element) &&
              (element.source === source || element.target === target)
            )
        )
      } else {
        const linkId = action.payload
        draft.flows[
          draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
        ].flowChart.elements = draft.flows[
          draft.workspaces[draft.selectedWorkspaceId].selectedFlowId
        ].flowChart.elements.filter(
          (element: FlowElement) => linkId !== element.id
        )
      }
      break
    case LOAD_WORKSPACE:
      draft.selectedWorkspaceId = action.payload
      break
    case CREATE_NEW_WORKSPACE:
      draft = _createNewWorkspace(draft)
      break
    case UPDATE_SELECTED_WORKSPACE: {
      const workspaceUpdate = action.payload
      if (draft.selectedWorkspaceId) {
        const selectedWorkspace = draft.workspaces[draft.selectedWorkspaceId]
        draft.workspaces[draft.selectedWorkspaceId] = {
          ...selectedWorkspace,
          ...workspaceUpdate,
        }
      }
      break
    }
    case DELETE_WORKSPACE: {
      const workspaceId = action.payload as string
      draft.workspaces = omit(draft.workspaces, workspaceId)
      draft.flows = pickBy(
        draft.flows,
        (flow) => flow.workspaceId !== workspaceId
      )

      const nonExampleWorkspaces = Object.entries(draft.workspaces).filter(
        ([id, workspace]: [string, Workspace]) => workspace.type !== "example"
      )

      if (
        draft.selectedWorkspaceId === workspaceId &&
        nonExampleWorkspaces.length
      ) {
        const idFirstNonExampleWorkspace = nonExampleWorkspaces[0][0]
        draft.selectedWorkspaceId = idFirstNonExampleWorkspace
      } else if (!nonExampleWorkspaces.length) {
        draft.workspaces = {
          ...cloneDeep(defaultWorkspaces),
          ...getExampleWorkspaces(),
        }
        draft.flows = {
          ...cloneDeep(defaultFlows),
          ...getExampleFlows(),
        }
        draft.selectedWorkspaceId = Object.keys(draft.workspaces)[0]
      }
      break
    }
  }
  saveFlowsToStorage(draft.flows)
  saveWorkspacesToStorage(draft.workspaces)
}, initialState)
Example #25
Source File: Table.tsx    From design-system with MIT License 4 votes vote down vote up
ArgsTable: FC<ArgsTableProps> = (props) => {
  const { error } = props as ArgsTableErrorProps;
  if (error) {
    return <EmptyBlock>{error}&nbsp;</EmptyBlock>;
  }

  const {
    rows,
    args,
    updateArgs,
    resetArgs,
    compact,
    inAddonPanel,
    initialExpandedArgs,
    sort = 'none',
  } = props as ArgsTableRowProps;

  const groups = groupRows(
    pickBy(rows, (row) => !row?.table?.disable),
    sort
  );

  if (
    groups.ungrouped.length === 0 &&
    Object.entries(groups.sections).length === 0 &&
    Object.entries(groups.ungroupedSubsections).length === 0
  ) {
    return (
      <EmptyBlock>
        No inputs found for this component.&nbsp;
      </EmptyBlock>
    );
  }

  let colSpan = 1;
  if (updateArgs) colSpan += 1;
  if (!compact) colSpan += 2;
  const expandable =
    Object.keys(groups.sections).length > 0;

  const common = {
    updateArgs,
    compact,
    inAddonPanel,
    initialExpandedArgs,
  };
  return (
    <TableWrapper
      {...{ compact, inAddonPanel }}
      className="docblock-argstable"
    >
      <thead className="docblock-argstable-head">
        <tr>
          <th>Name</th>
          {compact ? null : <th>Description</th>}
          {compact ? null : <th>Default</th>}
          {updateArgs ? (
            <th>
              <ControlHeadingWrapper>
                Control{' '}
                {resetArgs && (
                  <ResetButton
                    onClick={() => resetArgs()}
                    title="Reset controls"
                  ></ResetButton>
                )}
              </ControlHeadingWrapper>
            </th>
          ) : null}
        </tr>
      </thead>
      <tbody className="docblock-argstable-body">
        {groups.ungrouped.map((row) => (
          <ArgRow
            key={row.key}
            row={row}
            arg={args && args[row.key]}
            {...common}
          />
        ))}

        {Object.entries(groups.ungroupedSubsections).map(
          ([subcategory, subsection]) => (
            <SectionRow
              key={subcategory}
              label={subcategory}
              level="subsection"
              colSpan={colSpan}
            >
              {subsection.map((row) => (
                <ArgRow
                  key={row.key}
                  row={row}
                  arg={args && args[row.key]}
                  expandable={expandable}
                  {...common}
                />
              ))}
            </SectionRow>
          )
        )}

        {Object.entries(groups.sections).map(
          ([category, section]) => (
            <SectionRow
              key={category}
              label={category}
              level="section"
              colSpan={colSpan}
            >
              {section.ungrouped.map((row) => (
                <ArgRow
                  key={row.key}
                  row={row}
                  arg={args && args[row.key]}
                  {...common}
                />
              ))}
              {Object.entries(section.subsections).map(
                ([subcategory, subsection]) => (
                  <SectionRow
                    key={subcategory}
                    label={subcategory}
                    level="subsection"
                    colSpan={colSpan}
                  >
                    {subsection.map((row) => (
                      <ArgRow
                        key={row.key}
                        row={row}
                        arg={args && args[row.key]}
                        expandable={expandable}
                        {...common}
                      />
                    ))}
                  </SectionRow>
                )
              )}
            </SectionRow>
          )
        )}
      </tbody>
    </TableWrapper>
  );
}
Example #26
Source File: trace-querier.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
TraceInsightQuerier = () => {
  const [form] = Form.useForm();
  const [
    requestTraceParams,
    traceHistoryList,
    currentTraceRequestId,
    traceStatusDetail,
    traceDetailContent,
    spanDetailContent,
  ] = traceQuerierStore.useStore((s) => [
    s.requestTraceParams,
    s.traceHistoryList,
    s.currentTraceRequestId,
    s.traceStatusDetail,
    s.traceDetailContent,
    s.spanDetailContent,
  ]);
  const urlQuery = routeInfoStore.useStore((s) => s.query);
  const isShowTraceDetail = monitorCommonStore.useStore((s) => s.isShowTraceDetail);
  const { setIsShowTraceDetail } = monitorCommonStore.reducers;
  const [isTraceDetailContentFetching, isTraceHistoryFetching, isRequestTraceFetching] = useLoading(traceQuerierStore, [
    'getTraceDetailContent',
    'getTraceHistoryList',
    'requestTrace',
  ]);

  const {
    requestTrace,
    getSpanDetailContent,
    getTraceHistoryList,
    getTraceDetail,
    getTraceDetailContent,
    getTraceStatusDetail,
    cancelTraceStatus,
  } = traceQuerierStore.effects;

  const {
    setRequestTraceParams,
    setCurrentTraceRequestId,
    clearTraceStatusDetail,
    clearCurrentTraceRequestId,
    clearRequestTraceParams,
  } = traceQuerierStore.reducers;

  useEffectOnce(() => {
    getTraceHistoryList();
    return () => {
      clearRequestTraceParams();
      clearCurrentTraceRequestId();
      clearTraceStatusDetail();
    };
  });

  const { method, url, body, query, header, updateTime } = requestTraceParams;
  const queryStr = qs.stringify(query);
  const { validateFields } = form;
  const { requestId } = urlQuery;

  const [startTime, setStartTime] = React.useState(0);
  const [activeTab, setActiveTab] = React.useState('1');
  const [traceRecords, setTraceRecords] = React.useState({});
  const [inputUrl, setInputUrl] = React.useState('');
  let paramsEditor: any;
  let headersEditor: any;

  React.useEffect(() => {
    form.setFieldsValue({ body });
  }, [body]);

  React.useEffect(() => {
    requestId && setActiveTab('2');
  }, [requestId]);

  React.useEffect(() => {
    requestId &&
      getTraceDetailContent({ traceId: requestId, needReturn: true }).then((content: any) => {
        setTraceRecords(content);
      });
  }, [getTraceDetailContent, requestId]);

  React.useEffect(() => {
    form.setFieldsValue({ method });
  }, [method, form]);

  React.useEffect(() => {
    if (updateTime) {
      setStartTime(new Date(updateTime).getTime());
    }
  }, [updateTime]);

  const resetRequestTrace = () => {
    form.resetFields();
    clearRequestTraceParams();
    clearCurrentTraceRequestId();
    clearTraceStatusDetail();
  };

  const handleSetRequestTraceParams = (payload: any) => {
    return validateFields().then(() => {
      const params = { ...payload };
      const { query: preQuery, url: preUrl } = params;
      if (preUrl) {
        const queryMap = qs.parseUrl(preUrl).query;
        params.query = { ...preQuery, ...pickBy(queryMap, (v, k) => v && k) };
      }
      setRequestTraceParams({ ...requestTraceParams, ...params });
    });
  };

  const handleRequestTrace = () => {
    validateFields()
      .then(async () => {
        const payload: any = {};
        // 适应 AntD Tabs 组件 Tab Content 惰性加载取不到 ref 的问题
        if (paramsEditor) {
          payload.query = paramsEditor.getEditData();
        }

        if (headersEditor) {
          payload.header = headersEditor.getEditData();
        }
        await handleSetRequestTraceParams(payload);
        requestTrace({ startTime: new Date().getTime() });
      })
      .catch(() => {
        notify('warning', i18n.t('msp:param-error-check'));
      });
  };

  const renderMetaViewer = () => {
    return (
      <div className="meta-viewer">
        {!url ? (
          <p>{i18n.t('msp:Currently no requests')}</p>
        ) : (
          <p className="meta-viewer-copy">
            <Copy>{url}</Copy>
          </p>
        )}
      </div>
    );
  };

  const renderUrlEditor = React.useCallback(() => {
    const selectBefore = (
      <FormItem
        className="mb-0 -mt-0.5 h-7"
        name="method"
        initialValue={method}
        rules={[{ required: true, message: i18n.t('msp:this item is required') }]}
      >
        <Select
          bordered={false}
          style={{ width: 110 }}
          onSelect={(value) => {
            handleSetRequestTraceParams({ method: value });
          }}
        >
          {_map(HTTP_METHOD_LIST, (item) => (
            <Option value={item} key={item}>
              {item}
            </Option>
          ))}
        </Select>
      </FormItem>
    );

    return (
      <div className="url-editor">
        <Row gutter={10}>
          <Col span={18}>
            <FormItem
              className="m-0 h-8"
              name="url"
              rules={[{ required: true, message: i18n.t('msp:this item is required') }, urlRule]}
            >
              <Input
                addonBefore={selectBefore}
                placeholder={
                  i18n.t('msp|please enter a legal url, length limit: ', { nsSeparator: '|' }) +
                  MAX_URL_LENGTH.toString()
                }
                maxLength={MAX_URL_LENGTH}
                onChange={(e) => setInputUrl(e.target.value)}
                onBlur={(e) => {
                  handleSetRequestTraceParams({ url: e.target.value });
                }}
              />
            </FormItem>
          </Col>
          <Col span={6}>
            <Button
              type="primary"
              disabled={!inputUrl || !urlRule.pattern.test(inputUrl)}
              loading={!!url && isRequestTraceFetching}
              onClick={handleRequestTrace}
            >
              {i18n.t('msp:request')}
            </Button>
            <Popconfirm
              title={i18n.t('confirm to reset?')}
              placement="bottom"
              onConfirm={() => {
                resetRequestTrace();
              }}
            >
              <Button className="ml-4">{i18n.t('reset')}</Button>
            </Popconfirm>
          </Col>
        </Row>
      </div>
    );
  }, [inputUrl, url, requestTraceParams, isRequestTraceFetching]);

  const renderRequestEditor = () => {
    return (
      <Tabs className="request-editor" defaultActiveKey="1">
        <TabPane tab="Params" key="1">
          <div className="request-edit-params-form">
            <KeyValueEditor
              isNeedTextArea={false}
              tableProps={{
                size: 'default',
              }}
              form={form}
              dataSource={query}
              ref={(ref) => {
                paramsEditor = ref;
              }}
              onChange={(data: any) => {
                setRequestTraceParams({
                  ...requestTraceParams,
                  query: data,
                  url: `${qs.parseUrl(url).url}`,
                });
              }}
            />
          </div>
        </TabPane>
        <TabPane tab="Headers" key="2">
          <div className="request-edit-params-form">
            <KeyValueEditor
              tableProps={{
                size: 'default',
              }}
              form={form}
              dataSource={header}
              ref={(ref) => {
                headersEditor = ref;
              }}
            />
          </div>
        </TabPane>
        <TabPane tab="Body" key="3">
          <FormItem name="body" initialValue={body}>
            <TextArea
              className="request-edit-body-form"
              autoSize={{ minRows: 8, maxRows: 12 }}
              maxLength={MAX_BODY_LENGTH}
              placeholder={
                i18n.t('msp|please enter body, length limit:', { nsSeparator: '|' }) + MAX_BODY_LENGTH.toString()
              }
              onChange={(e) => {
                handleSetRequestTraceParams({ body: e.target.value });
              }}
            />
          </FormItem>
        </TabPane>
      </Tabs>
    );
  };

  const renderStatusList = () => {
    return (
      <CommonPanel
        title={
          <div className="flex justify-between items-center">
            <div className="flex">
              <h3 className="trace-common-panel-title font-medium mr-2">
                {firstCharToUpper(i18n.t('msp:tracing information'))}
              </h3>
              {traceStatusDetail?.status === 1 && (
                <Tooltip title={i18n.t('Full screen')}>
                  <ErdaIcon
                    type="full-screen-one"
                    size="14"
                    className="cursor-pointer hover:text-primary mb-2"
                    onClick={() => setIsShowTraceDetail(true)}
                  />
                </Tooltip>
              )}
            </div>
            <IF check={requestTraceParams.responseCode}>
              <div className="response-code">{`${i18n.t('msp:request response status')}:${
                requestTraceParams.responseCode
              }`}</div>
            </IF>
          </div>
        }
        className="trace-status-list-ct"
      >
        <RequestStatusViewer
          traceStatusDetail={traceStatusDetail}
          cancelTraceStatus={cancelTraceStatus}
          spanDetailContent={spanDetailContent}
          traceDetailContent={traceDetailContent}
          isTraceDetailContentFetching={isTraceDetailContentFetching}
          getSpanDetailContent={getSpanDetailContent}
        />
      </CommonPanel>
    );
  };

  return (
    <div>
      <Row className="trace-querier" gutter={20}>
        <Col span={6}>
          <CommonPanel title={i18n.t('msp:Query records')} className="history-status-list-ct">
            <TraceHistoryList
              dataSource={traceHistoryList}
              isFetching={isTraceHistoryFetching}
              currentTraceRequestId={currentTraceRequestId}
              setInputUrl={setInputUrl}
              getTraceHistoryList={getTraceHistoryList}
              getTraceDetail={getTraceDetail}
              getTraceStatusDetail={getTraceStatusDetail}
              setCurrentTraceRequestId={setCurrentTraceRequestId}
              setRequestTraceParams={setRequestTraceParams}
              clearTraceStatusDetail={clearTraceStatusDetail}
              clearCurrentTraceRequestId={clearCurrentTraceRequestId}
              clearRequestTraceParams={clearRequestTraceParams}
              form={form}
            />
          </CommonPanel>
        </Col>
        <Col span={18}>
          <CommonPanel>
            <React.Fragment>
              {renderMetaViewer()}
              <Form form={form}>
                {renderUrlEditor()}
                {renderRequestEditor()}
              </Form>
            </React.Fragment>
          </CommonPanel>
          {renderStatusList()}
        </Col>
      </Row>
      {isShowTraceDetail && <TraceSearchDetail traceId={traceStatusDetail?.requestId} startTime={startTime} />}
    </div>
  );
}
Example #27
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ApiAssetDetail = () => {
  const [visible, setVisible] = React.useState(false);
  const [versionListVisible, setVersionListVisible] = React.useState(false);
  const { assetID, versionID } = routeInfoStore.useStore((s) => s.params);
  const [assetVersionDetail, assetDetail, hasAccess, version, assetVersionList] = apiMarketStore.useStore((s) => [
    s.assetVersionDetail.spec,
    s.assetVersionDetail.asset,
    s.assetVersionDetail.hasAccess,
    s.assetVersionDetail.version,
    s.assetVersionList,
  ]);
  const { getAssetVersionDetail, getListOfVersions } = apiMarketStore.effects;
  const { clearState } = apiMarketStore.reducers;
  const [isLoading, isFetchVersionList] = useLoading(apiMarketStore, ['getAssetVersionDetail', 'getListOfVersions']);
  React.useEffect(() => {
    if (assetID && versionID) {
      getAssetVersionDetail({ assetID, versionID, asset: true, spec: true });
    }
    return () => {
      clearState({ key: 'assetVersionDetail', value: { asset: {}, spec: {}, version: {}, access: {} } });
      clearState({ key: 'assetVersionList', value: [] });
    };
  }, [assetID, clearState, getAssetVersionDetail, versionID]);

  const currentVersion = React.useMemo((): API_MARKET.AssetVersionItem<OpenAPI.Document> => {
    if (!assetVersionDetail.spec) {
      return {} as API_MARKET.AssetVersionItem<OpenAPI.Document>;
    }
    let spec = {} as OpenAPI.Document;
    try {
      if (['oas2-yaml', 'oas3-yaml'].includes(assetVersionDetail.specProtocol)) {
        spec = yaml.load(assetVersionDetail.spec);
      } else if (['oas2-json', 'oas3-json'].includes(assetVersionDetail.specProtocol)) {
        spec = JSON.parse(assetVersionDetail.spec);
      }
    } catch (e) {
      message.error(i18n.t('default:failed to parse API description document'));
    }
    return {
      spec,
    };
  }, [assetVersionDetail.spec, assetVersionDetail.specProtocol]);

  const handleChangeVersion = (id: number) => {
    if (id) {
      goTo(`../${id}`);
    }
  };

  const handleApply = () => {
    setVisible(true);
  };

  const showVersionList = () => {
    getListOfVersions({ major: version.major, minor: version.minor, spec: false, assetID }).then(() => {
      setVersionListVisible(true);
    });
  };

  const handleExport = (specProtocol: API_MARKET.SpecProtocol, id: number) => {
    window.open(exportSwagger({ assetID, versionID: id, specProtocol }));
  };

  const columns: Array<ColumnProps<API_MARKET.VersionItem>> = [
    {
      title: i18n.t('default:version number'),
      dataIndex: ['version', 'major'],
      width: 140,
      render: (_text, { version: { major, minor, patch } }) => `${major}.${minor}.${patch}`,
    },
    {
      title: i18n.t('API description document protocol'),
      dataIndex: ['version', 'specProtocol'],
      width: 200,
      render: (text) => protocolMap[text].fullName,
    },
    {
      title: i18n.t('Creator'),
      dataIndex: ['version', 'creatorID'],
      width: 200,
      render: (text) => <Avatar showName name={<UserInfo id={text} />} />,
    },
    {
      title: i18n.t('Creation time'),
      dataIndex: ['version', 'createdAt'],
      width: 200,
      render: (text) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : ''),
    },
  ];

  const tableAction: IActions<API_MARKET.VersionItem> = {
    render: (record) => {
      const filterProtocolMap = pickBy(protocolMap, (_, protocol) => {
        const protocolPrefix = record.version?.specProtocol?.substr(0, 4) || '';
        return protocol.indexOf(protocolPrefix) > -1;
      });
      return map(filterProtocolMap, ({ name }, key: API_MARKET.SpecProtocol) => {
        return {
          title: i18n.t('export {type}', { type: name }),
          onClick: () => {
            handleExport(key, record.version.id);
          },
        };
      });
    },
  };

  return (
    <div className="api-market-detail full-spin-height">
      <TopButtonGroup>
        <Button onClick={showVersionList}>{i18n.t('version list')}</Button>
        {hasAccess ? (
          <Button type="primary" onClick={handleApply}>
            {i18n.t('apply to call')}
          </Button>
        ) : null}
      </TopButtonGroup>
      <Spin wrapperClassName="api-market-detail-loading" spinning={isLoading || isFetchVersionList}>
        <ApiView
          deprecated={version.deprecated}
          dataSource={currentVersion}
          onChangeVersion={handleChangeVersion}
          specProtocol={version.specProtocol}
        />
      </Spin>
      <ApplyModal
        visible={visible}
        onCancel={() => {
          setVisible(false);
        }}
        dataSource={assetDetail as API_MARKET.Asset}
      />
      <Modal
        width={960}
        title={`${i18n.t('version list')}(${version.major}.${version.minor}.*)`}
        visible={versionListVisible}
        onCancel={() => {
          setVersionListVisible(false);
        }}
        footer={null}
      >
        <Table
          loading={isFetchVersionList}
          rowKey={({ version: { major, minor, patch } }) => `${major}-${minor}-${patch}`}
          columns={columns}
          dataSource={assetVersionList}
          onChange={() => {
            getListOfVersions({ major: version.major, minor: version.minor, spec: false, assetID });
          }}
          actions={tableAction}
          scroll={{ x: '100%' }}
        />
      </Modal>
    </div>
  );
}
Example #28
Source File: addon-card-list.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
AddonCardList = (props: IProps) => {
  const [dataSource, setDataSource] = React.useState([] as any[]);
  const [searchKey, setSearchKey] = React.useState('');
  const [name, setName] = React.useState('');
  const [dataSourceType, setDataSourceType] = React.useState('ALL');
  const [env, setEnv] = React.useState('ALL');
  const [activeCategory, setActiveCategory] = React.useState();
  const categoryRefs = React.useRef([] as any[]);
  const categoryCardsRef = React.useRef(null);
  const { searchPlaceHolder, addonCategory, isFetching, onEitAddon, isPlatform } = props;

  const debounceCheck = React.useCallback(
    debounce(() => {
      const categoryList = categoryCardsRef.current as any;
      categoryRefs.current.forEach((categorySection: any) => {
        const { current } = head(Object.values(categorySection)) as any;
        if (current) {
          const top = current.offsetTop - categoryList.scrollTop - 52;
          const newActiveCategory = head(Object.keys(categorySection));
          if (top <= 0) {
            setActiveCategory(newActiveCategory);
          }
        }
      });
    }, 100),
    [],
  );

  const operateScrollEvent = (isRemove?: boolean) => {
    const categoryList = categoryCardsRef.current as any;
    if (categoryList) {
      !isRemove
        ? categoryList.addEventListener('scroll', debounceCheck)
        : categoryList.removeEventListener('scroll', debounceCheck);
    }
  };

  const scrollToTarget = (targetCategoryName: string) => {
    const targetRef = categoryRefs.current.find((ref: any) => ref[targetCategoryName]);
    const targetCategoryDom = targetRef[targetCategoryName].current;
    if (!targetCategoryDom) {
      return;
    }
    targetCategoryDom.parentNode.scrollTop = targetCategoryDom.offsetTop - 20;
  };

  React.useEffect(() => {
    if (!isEmpty(props.addonCategory) && !isEmpty(dataSource)) {
      const firstCategory = head(dataSource) as any[];
      const targetCategory = head(firstCategory);
      scrollToTarget(targetCategory);
    }
  }, [dataSource, props.addonCategory]);

  React.useEffect(() => {
    operateScrollEvent();
    return () => {
      operateScrollEvent(true);
    };
  });
  useUpdateEffect(() => {
    const { addonList = [], searchProps } = props;
    if (!isEmpty(addonList)) {
      setDataSource(
        addonList.filter((addon: ADDON.Instance) => {
          const isOtherFilter =
            (addon.workspace === env || env === 'ALL') &&
            searchFilter(addon, searchKey, searchProps) &&
            searchFilter(addon, name, searchProps);

          if (dataSourceType === 'custom') {
            const isCustomDataSource = CustomDataSourceMap.includes(addon.displayName);
            return isOtherFilter && isCustomDataSource;
          }
          return isOtherFilter && (addon.displayName?.toLowerCase() === dataSourceType || dataSourceType === 'ALL');
        }),
      );
    } else if (!isEmpty(addonCategory)) {
      const cloneAddonCategory = cloneDeep(addonCategory);
      Object.keys(cloneAddonCategory).forEach((key: string) => {
        const value = cloneAddonCategory[key];
        cloneAddonCategory[key] = value.filter(
          (addon: IAddon) => (addon.workspace === env || env === 'ALL') && searchFilter(addon, searchKey, searchProps),
        );
      });
      const filterDataSource = Object.entries(pickBy(cloneAddonCategory, (x: any[]) => x.length > 0));
      const resolvedFilterDataSource: any[] = [];
      const categories = Object.keys(addonCategory);
      forEach(categories, (v) => {
        const targetItem = find(filterDataSource, (item) => item[0] === v);
        targetItem && resolvedFilterDataSource.push(targetItem);
      });
      setDataSource(resolvedFilterDataSource);
      categoryRefs.current = Object.keys(cloneAddonCategory).map((key: string) => {
        return { [key]: React.createRef() };
      });
      setActiveCategory(head(head(resolvedFilterDataSource)));
    } else {
      setDataSource([]);
    }
  }, [env, searchKey, props.addonList, props.addonCategory, props, name, dataSourceType]);

  const onSearchKeyChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = get(event, 'target.value');
    setSearchKey(value);
  };

  const onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = get(event, 'target.value');
    setName(value);
  };

  const onEnvChange = (value: string) => {
    setEnv(value);
  };

  const onDataSourceChange = (value: any) => {
    setDataSourceType(value);
  };

  const onClickCategory = (event: React.MouseEvent) => {
    const targetCategory = event.currentTarget.getAttribute('value') as string;
    if (activeCategory === targetCategory) {
      return;
    }
    setActiveCategory(targetCategory);
    scrollToTarget(targetCategory);
  };

  const renderCategoryList = () => {
    if (addonCategory) {
      const categories = Object.keys(addonCategory);
      const resolvedCategories = categories.filter((v) => CategoriesOrder.includes(v));
      return resolvedCategories.map((key: string) => {
        key = allWordsFirstLetterUpper(key);
        return (
          <li
            key={key}
            className={`category-item cursor-pointer ${activeCategory === key ? 'active' : ''}`}
            value={key}
            onClick={onClickCategory}
          >
            <Tooltip title={key}>{key}</Tooltip>
          </li>
        );
      });
    }
    return null;
  };

  return (
    <section className="addon-card-list">
      <Spin wrapperClassName="full-spin-height" spinning={isFetching}>
        <div className="addon-filter">
          {!props.showDataSourceSearch && (
            <Select className="env-select" defaultValue="ALL" onChange={onEnvChange}>
              {envOptions}
            </Select>
          )}
          {props.hideSearch ? null : (
            <Search className="data-select" onChange={onSearchKeyChange} placeholder={searchPlaceHolder} />
          )}
          {props.showDataSourceSearch && (
            <Search className="data-select mr-5" onChange={onNameChange} placeholder={searchPlaceHolder} />
          )}
          {props.showDataSourceSelect && (
            <Select className="data-select" defaultValue="ALL" onChange={onDataSourceChange}>
              {dataSourceTypeOptions}
            </Select>
          )}
        </div>
        <div className="addons-content">
          <IF check={!isEmpty(addonCategory)}>
            <div className="addon-menu">
              <span className="content-title font-medium">{firstCharToUpper(i18n.t('dop:addon category'))}</span>
              <ul className="menu-list">{renderCategoryList()}</ul>
            </div>
          </IF>
          <AddonCards
            forwardedRef={categoryCardsRef}
            dataSource={dataSource}
            onEitAddon={onEitAddon}
            isPlatform={isPlatform}
            categoryRefs={categoryRefs.current}
          />
        </div>
      </Spin>
    </section>
  );
}
Example #29
Source File: page-render.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ConfigPageRender = (props: IProps) => {
  const { pageConfig, customProps, forceUpdateCustom, execOperation, changeScenario, updateState } = props;
  const { hierarchy, components = emptyObj, options } = pageConfig || {};
  const [componentsKey, setComponentsKey] = React.useState([] as string[]);
  const routeParams = routeInfoStore.useStore((s) => s.params);
  const containerMapRef = React.useRef(null as any);

  const customPropsUpdateMark = forceUpdateCustom ? customProps : '';
  React.useEffect(() => {
    if (components) {
      const curCompKeys = Object.keys(components);
      if (!isEqual(curCompKeys, componentsKey)) {
        containerMapRef.current = getContainerMap(components, customProps);
        setComponentsKey(curCompKeys);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [components, customPropsUpdateMark]);

  if (isEmpty(pageConfig)) return <EmptyHolder relative />;

  const containerMap = containerMapRef.current || {};
  const { root, structure = {} } = hierarchy || {};

  const compPrefixKey = 'protocol.components';
  const reUpdateState = (_cId: string) => (val: Obj) => {
    return updateState(`${compPrefixKey}.${_cId}.state`, val);
  };

  const handleClickGoto = (_op: CP_COMMON.Operation) => {
    const { serverData } = _op;
    const { params, query, target, jumpOut } = serverData || {};
    const str_num_params = pickBy({ ...params }, (v) => ['string', 'number'].includes(typeof v));
    const str_num_query = pickBy(query, (v) => ['string', 'number'].includes(typeof v));
    const targetPath = goTo.pages[target] || target;
    targetPath && goTo(targetPath, { ...routeParams, ...str_num_params, jumpOut, query: str_num_query });
  };

  const execCommand = (command: Obj, val: Obj) => {
    if (command) {
      const { key, state, target, jumpOut } = command;
      if (key === 'goto' && target) {
        const { params, query } = state || {};
        const str_num_params = pickBy({ ...(val || {}), ...params }, (v) => ['string', 'number'].includes(typeof v));
        const str_num_query = pickBy(query, (v) => ['string', 'number'].includes(typeof v));
        goTo(goTo.pages[target] || target, { ...routeParams, ...str_num_params, jumpOut, query: str_num_query });
        return true;
      } else if (key === 'changeScenario') {
        changeScenario(command);
        return true;
      }
    }
    return false;
  };

  const reExecOperation = (_cId: string) => (_op: any, val: any) => {
    if (!_op || isEmpty(_op)) return;
    if (_op.disabled) {
      const tip = _op.disabledTip || _op.tip;
      tip && message.error(tip);
      return;
    }
    if (_op.key === 'clickGoto') {
      return handleClickGoto(_op);
    }

    const op = cloneDeep({ ..._op });
    let updateVal = cloneDeep(val) as any;
    if (op.fillMeta) {
      // 有fillMeta,需要手动设置op.meta
      updateVal = undefined;
      const [fillAttrs, attrVals] = isArray(op.fillMeta)
        ? [op.fillMeta, { ...val }]
        : [[op.fillMeta], { [op.fillMeta]: val }];

      map(fillAttrs, (attr) => {
        set(op, `meta.${attr}`, attrVals[attr]);
      });
    }
    if (op.reload === false) {
      if (op.command) {
        const { state, target } = op.command;
        if (execCommand(op.command, updateVal)) return;
        return execOperation(_cId, op, { dataKey: `${compPrefixKey}.${target}.state`, dataVal: state });
      }
      return;
    }
    if (op.command?.key) {
      op.callBack = () => execCommand(op.command, updateVal);
    }

    return execOperation(
      _cId,
      op,
      isEmpty(updateVal) ? undefined : { dataKey: `${compPrefixKey}.${_cId}.state`, dataVal: updateVal },
    );
  };

  if (root) {
    const renderComp = (cId: string): any => {
      const structureItem = (structure || {})[cId];
      const Comp = containerMap[cId] as any;
      if (!Comp) return null;
      const configComponent = get(pageConfig, `components.${cId}`) || {};
      const { op, props: customComponentProps, ...restCustomConfig } = customProps?.[cId] || {};
      const { operations: dataOp, ...restData } = configComponent.data || {};
      const enhanceProps = {
        globalOptions: options,
        ...restCustomConfig,
        ...configComponent,
        data: restData,
        operations: { ...configComponent.operations, ...dataOp },
        props: { ...customComponentProps, ...configComponent.props },
        key: cId,
        cId,
        customOp: op || emptyObj,

        updateState: reUpdateState(cId),
        execOperation: reExecOperation(cId),
      };

      if (isArray(structureItem)) {
        // 数组: 包含以children方式嵌入组件
        return (
          <EnhanceCompProps {...enhanceProps}>
            <Comp>{map(structureItem, (item) => renderComp(item))}</Comp>
          </EnhanceCompProps>
        );
      } else if (!structureItem) {
        // 叶子节点,直接渲染
        return (
          <EnhanceCompProps {...enhanceProps}>
            <Comp />
          </EnhanceCompProps>
        );
      } else if (isPlainObject(structureItem)) {
        // 对象包含:以props方式嵌入组件
        const p = {};
        map(structureItem, (vKey, pKey) => {
          if (isArray(vKey)) {
            p[pKey] = [];
            map(vKey, (vItem) => {
              p[pKey].push(renderComp(vItem));
            });
          } else {
            p[pKey] = renderComp(vKey);
          }
        });
        return (
          <EnhanceCompProps {...enhanceProps}>
            <Comp {...p} />
          </EnhanceCompProps>
        );
      }
      return null;
    };
    return <>{renderComp(root)}</>;
  } else {
    // eslint-disable-next-line no-console
    console.log('dice config page: 请配置一个root节点');
    return null;
  }
}