lodash#differenceBy TypeScript Examples

The following examples show how to use lodash#differenceBy. 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: assetsDbService.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
public async sync(assets: IDbAsset[], walletId: number): Promise<void> {
    const groups = groupBy(assets, (a) => a.address);
    const dbGroups = groupBy(await this.getByWalletId(walletId), (a) => a.address);
    const groupKeys = union(keys(dbGroups), keys(groups));

    for (const key of groupKeys) {
      const group = groups[key];
      const dbGroup = dbGroups[key];

      if (isEmpty(dbGroup) && isEmpty(groups)) {
        continue;
      } else if (isEmpty(group) && !isEmpty(dbGroup)) {
        await dbContext.assets.bulkDelete(this.primaryKeysFrom(dbGroup));
        continue;
      } else if (isEmpty(dbGroup)) {
        await dbContext.assets.bulkPut(group);
        continue;
      }

      const remove = this.primaryKeysFrom(differenceBy(dbGroup, group, (a) => a.tokenId));
      const put = this.newOrChanged(dbGroup, group);
      if (remove.length > 0) {
        await dbContext.assets.bulkDelete(remove);
      }
      if (put.length > 0) {
        await dbContext.assets.bulkPut(put);
      }
    }
  }
Example #2
Source File: CompareView.tsx    From hub with Apache License 2.0 4 votes vote down vote up
CompareView = (props: Props) => {
  const [diffTemplates, setDiffTemplates] = useState<ChartTemplate[] | null | undefined>();
  const [activeTemplate, setActiveTemplate] = useState<CompareChartTemplate | null>(null);
  const [isChangingTemplate, setIsChangingTemplate] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [visibleTemplates, setVisibleTemplates] = useState<CompareChartTemplate[] | undefined>();
  const [expanded, setExpanded] = useState<boolean>(false);

  const onTemplateChange = (template: CompareChartTemplate | null) => {
    setIsChangingTemplate(true);
    setExpanded(false);
    setActiveTemplate(template);
    props.updateUrl({ template: template ? template.name : undefined, compareTo: props.comparedVersion });
  };

  useEffect(() => {
    async function getDiffCompareChartTemplates(version: string) {
      try {
        setIsChangingTemplate(true);
        setIsLoading(true);
        const data = await API.getChartTemplates(props.packageId, version);
        if (data && data.templates) {
          const formattedTemplates: ChartTemplate[] = props.formatTemplates(data.templates);
          if (formattedTemplates.length > 0) {
            setDiffTemplates(formattedTemplates);
          } else {
            setDiffTemplates([]);
          }
        }
      } catch {
        setDiffTemplates(null);
        setIsChangingTemplate(false);
      } finally {
        setIsLoading(false);
      }
    }

    if (props.comparedVersion !== '') {
      getDiffCompareChartTemplates(props.comparedVersion);
    } else {
      setDiffTemplates([]);
    }
  }, [props.comparedVersion]); /* eslint-disable-line react-hooks/exhaustive-deps */

  useEffect(() => {
    const prepareVisibleTemplates = () => {
      let tmpls: CompareChartTemplate[] = [];
      props.templates!.forEach((tmpl: ChartTemplate) => {
        const diffTmpl = diffTemplates!.find((t: ChartTemplate) => t.name === tmpl.name);
        if (diffTmpl) {
          if (diffTmpl.data !== tmpl.data) {
            tmpls.push({ ...tmpl, compareData: diffTmpl.data, status: CompareChartTemplateStatus.Modified });
          }
        } else {
          tmpls.push({
            ...tmpl,
            compareData: '',
            status: CompareChartTemplateStatus.Added,
          });
        }
      });
      const others = differenceBy(diffTemplates!, props.templates!, 'name');
      others.forEach((tmpl: ChartTemplate) => {
        tmpls.push({
          ...tmpl,
          data: '',
          compareData: tmpl.data,
          status: CompareChartTemplateStatus.Deleted,
        });
      });
      const sortedTmpls: CompareChartTemplate[] = sortBy(tmpls, ['type', 'name']);
      setVisibleTemplates(sortedTmpls);
      if (sortedTmpls.length === 0) {
        setActiveTemplate(null);
      } else {
        if (props.visibleTemplate) {
          const selectedTmpl = sortedTmpls.find((tmpl: CompareChartTemplate) => props.visibleTemplate === tmpl.name);
          if (selectedTmpl) {
            setActiveTemplate(selectedTmpl);
          } else {
            setActiveTemplate(sortedTmpls[0]);
          }
        } else {
          setActiveTemplate(sortedTmpls[0]);
        }
      }
    };

    if (diffTemplates && !isNull(props.templates)) {
      prepareVisibleTemplates();
      setIsChangingTemplate(false);
    }
  }, [diffTemplates]); /* eslint-disable-line react-hooks/exhaustive-deps */

  return (
    <div className="d-flex flex-row align-items-stretch g-0 h-100 mh-100">
      <div className="col-3 h-100">
        <CompareTemplatesList
          templates={visibleTemplates}
          activeTemplateName={activeTemplate ? activeTemplate.name : undefined}
          onTemplateChange={onTemplateChange}
        />
      </div>

      <div className="col-9 ps-3 h-100">
        <div className={`position-relative h-100 mh-100 border ${styles.templateWrapper}`}>
          {((isChangingTemplate && activeTemplate) || isLoading) && <Loading />}
          <div className={`position-absolute d-flex ${styles.wrapper}`}>
            <div className="position-relative">
              <button
                className={`btn btn-sm btn-primary rounded-circle fs-5 ${styles.btn}`}
                onClick={() => {
                  setExpanded(!expanded);
                }}
                aria-label={`${expanded ? 'Collapse' : 'Expand'} code`}
                disabled={!isUndefined(visibleTemplates) && visibleTemplates.length === 0}
              >
                {expanded ? <BsArrowsCollapse /> : <BsArrowsExpand />}
              </button>
            </div>
          </div>

          <pre className={`text-muted h-100 mh-100 mb-0 overflow-hidden position-relative diffTemplate ${styles.pre}`}>
            {!isUndefined(visibleTemplates) && visibleTemplates.length === 0 && (
              <div className="d-flex align-items-center justify-content-center h-100 w-100 p-5">
                <div className={`alert alert-dark px-5 py-4 text-center ${styles.alert}`}>
                  <span className="text-muted">
                    No changes found when comparing version <span className="fw-bold">{props.currentVersion}</span> to{' '}
                    <span className="fw-bold">{props.comparedVersion}</span>
                  </span>
                </div>
              </div>
            )}
            {activeTemplate && (
              <ErrorBoundary className={styles.errorAlert} message="Something went wrong rendering the template.">
                <DiffTemplate
                  currentVersion={props.currentVersion}
                  diffVersion={props.comparedVersion}
                  template={activeTemplate!}
                  expanded={expanded}
                  setIsChangingTemplate={setIsChangingTemplate}
                />
              </ErrorBoundary>
            )}
          </pre>
        </div>
      </div>
    </div>
  );
}
Example #3
Source File: BrickTag.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function BrickTag(props: BrickTagProps): React.ReactElement {
  const {
    componentType,
    tagList,
    configProps,
    defaultCheckedTag,
    showTagCircle,
    color,
    closable,
    cancelable,
    tooltipProps,
  } = props;

  const [checkedTag, setCheckedTag] = useState([]);
  const [closedTag, setClosedTag] = useState(tagList);

  useEffect(() => {
    setCheckedTag(
      defaultCheckedTag
        ? isArray(defaultCheckedTag)
          ? defaultCheckedTag
          : [defaultCheckedTag]
        : []
    );
  }, [defaultCheckedTag]);

  const [hoverTag, setHoverTag] = useState(null);

  const onChange = (item, checked) => {
    const key = item.key;
    let nextCheckedTag: string[];
    if (props.multipleCheck) {
      nextCheckedTag = checked
        ? [...checkedTag, key]
        : checkedTag.filter((t) => t !== key);
    } else {
      if (!cancelable && !checked) {
        return;
      }
      nextCheckedTag = checked ? [key] : [];
    }
    const nextCheckedItems = tagList?.filter((item) =>
      nextCheckedTag?.includes(item.key)
    );
    props.handleOnChange(nextCheckedItems);
  };

  const onClose = (item) => {
    const newClosedTag = differenceBy(closedTag, [item], "key");
    setClosedTag(newClosedTag);
    props?.handleOnClose(item, newClosedTag);
  };

  const onClick = (item) => {
    props?.handleOnClick(item);
  };

  const onMouseEnter = (item, e) => {
    setHoverTag(item.key);
  };

  const onMouseLeave = (item, e) => {
    setHoverTag(null);
  };

  const renderTag = (TypeComponent: any, tagList: TagListType[]) =>
    tagList.map((item) => {
      const {
        key,
        label,
        tooltip,
        icon,
        color: itemColor,
        disabled,
        disabledTooltip,
      } = item;
      let restProps: any = {};
      const hover = hoverTag === key;
      if (TypeComponent === Tag) {
        restProps = {
          style: {
            ...props.tagStyle,
            ...(hover ? props.tagHoverStyle : {}),
          },
          onClick: () => {
            onClick(item);
          },
        };
      } else {
        const checked = checkedTag.includes(key);
        restProps = {
          style: {
            ...props.tagStyle,
            ...(checked ? props.tagCheckedStyle : {}),
            ...(hover ? props.tagHoverStyle : {}),
          },
          checked,
          onChange: (c) => onChange(item, c),
        };
      }
      const specificColor = itemColor || color;
      const tagNode = (
        <TypeComponent
          key={key}
          className={classNames({
            [style.grayTag]: specificColor === "gray" || disabled,
            [style.grayInverseTag]: specificColor === "gray-inverse",
            [style.round]: props.shape === "round",
            [style.closableTag]: closable && TypeComponent === Tag,
            [style.tagCircleIcon]: showTagCircle,
            [style.colorTag]:
              specificColor && !closable && TypeComponent === Tag,
            [style.disabledTag]: disabled,
          })}
          closable={closable}
          {...(closable ? { onClose: () => onClose(item) } : {})}
          color={!closable && specificColor}
          {...configProps}
          {...restProps}
          onMouseEnter={(e) => onMouseEnter(item, e)}
          onMouseLeave={(e) => onMouseLeave(item, e)}
        >
          {!showTagCircle &&
            icon &&
            (typeof icon === "string" ? (
              <LegacyIcon
                type={icon}
                style={{ marginRight: "7px", marginLeft: 0 }}
              />
            ) : (
              typeof icon === "object" && (
                <GeneralIcon
                  icon={icon}
                  style={{ marginRight: "7px", marginLeft: 0 }}
                />
              )
            ))}
          {showTagCircle && (
            <GeneralIcon
              icon={circleIcon}
              style={{ marginRight: "7px", marginLeft: 0 }}
            />
          )}
          {label}
        </TypeComponent>
      );
      return tooltip ||
        (disabled && (props.disabledTooltip || disabledTooltip)) ? (
        <Tooltip
          key={key}
          title={disabled ? disabledTooltip || props.disabledTooltip : tooltip}
          {...tooltipProps}
        >
          {tagNode}
        </Tooltip>
      ) : (
        tagNode
      );
    });

  return (
    <div
      className={classNames(style.brickTag, {
        [style.tagTextOverflow]: props.textEllipsis,
      })}
    >
      {props.label && <span className={style.tagsLabel}>{props.label}</span>}
      {componentType === TagTypeProps.CheckableTag
        ? renderTag(Tag.CheckableTag, tagList)
        : renderTag(Tag, tagList)}
    </div>
  );
}
Example #4
Source File: TicketAssignments.tsx    From condo with MIT License 4 votes vote down vote up
TicketAssignments = ({
    validations,
    organizationId,
    propertyId,
    disableUserInteraction,
    autoAssign,
    categoryClassifier,
    form,
}: TicketAssignmentsProps) => {
    const intl = useIntl()
    const TicketAssignmentTitle = intl.formatMessage({ id: 'TicketAssignment' })
    const ExecutorLabel = intl.formatMessage({ id: 'field.Executor' })
    const ResponsibleLabel = intl.formatMessage({ id: 'field.Responsible' })
    const ExecutorExtra = intl.formatMessage({ id: 'field.Executor.description' })
    const ResponsibleExtra = intl.formatMessage({ id: 'field.Responsible.description' })
    const ExecutorsOnThisDivisionLabel = intl.formatMessage({ id: 'ticket.assignments.executor.OnThisDivision' })
    const ExecutorsOnOtherDivisionsLabel = intl.formatMessage({ id: 'ticket.assignments.executor.OnOtherDivisions' })
    const OtherExecutors = intl.formatMessage({ id: 'ticket.assignments.executor.Other' })

    const { isSmall } = useLayoutContext()

    const [divisions, setDivisions] = useState([])

    const formatUserFieldLabel = ({ text, value, data: employee }) => {
        if (!employee) {
            return null
        }
        const matchedSpecialization = find(employee.specializations, { id: categoryClassifier })
        return (
            <UserNameField user={{ name: text, id: value }}>
                {({ name, postfix }) => (
                    <>
                        <Typography.Text>
                            {name} {postfix}
                        </Typography.Text>
                        {matchedSpecialization && (
                            <Typography.Text type="secondary">
                                {`(${matchedSpecialization.name.toLowerCase()})`}
                            </Typography.Text>
                        )}
                    </>
                )}
            </UserNameField>
        )
    }

    const getTechniciansFrom = (division) => (
        division.executors.filter(({ specializations }) => (
            specializations.some(({ id }) => id === categoryClassifier)
        ))
    )

    const convertToOption = (employee) => ({
        text: employee.name,
        value: employee.user.id,
        data: employee,
    })

    /**
     * Employees are grouped by following rules:
     * 1. Technicians with matched specialization, belonging to matched division;
     * 2. Technicians with matched specialization, belonging to other matched divisions;
     * 3. Rest of employees.
     */
    const renderOptionGroups = (employeeOptions, renderOption) => {
        const [currentDivision, ...otherDivisions] = divisions
        let techniciansOnDivisionOptions = []
        let techniciansOnOtherDivisionsOptions = []

        if (currentDivision) {
            const techniciansOnDivision = getTechniciansFrom(currentDivision)
            techniciansOnDivisionOptions = techniciansOnDivision.map(convertToOption)

            const techniciansOnOtherDivisions =
                differenceBy(
                    uniqBy(
                        otherDivisions.reduce((acc, otherDivision) => ([
                            ...acc,
                            ...getTechniciansFrom(otherDivision),
                        ]), []),
                        'id',
                    ),
                    techniciansOnDivision,
                    'id',
                )

            techniciansOnOtherDivisionsOptions = techniciansOnOtherDivisions.map(convertToOption)
        }

        const otherTechniciansOptions = differenceBy(employeeOptions, [
            ...techniciansOnDivisionOptions,
            ...techniciansOnOtherDivisionsOptions,
        ], 'value')

        const result = []
        if (!currentDivision || techniciansOnDivisionOptions.length === 0 && techniciansOnOtherDivisionsOptions.length === 0) {
            result.push(otherTechniciansOptions.map(renderOption))
        } else {
            if (techniciansOnDivisionOptions.length > 0) {
                result.push(
                    <Select.OptGroup label={ExecutorsOnThisDivisionLabel}>
                        {techniciansOnDivisionOptions.map(renderOption)}
                    </Select.OptGroup>
                )
            }
            if (techniciansOnOtherDivisionsOptions.length > 0) {
                result.push(
                    <Select.OptGroup label={ExecutorsOnOtherDivisionsLabel}>
                        {techniciansOnOtherDivisionsOptions.map(renderOption)}
                    </Select.OptGroup>
                )
            }
            if (otherTechniciansOptions.length > 0) {
                result.push(
                    <Select.OptGroup label={OtherExecutors}>
                        {otherTechniciansOptions.map(renderOption)}
                    </Select.OptGroup>
                )
            }
        }

        return result
    }

    return (
        <Col span={24}>
            <Row gutter={[0, 8]}>
                <Col span={24}>
                    <Typography.Title level={3}>{TicketAssignmentTitle}</Typography.Title>
                </Col>
                <Col span={isSmall ? 24 : 18}>
                    <Row justify={'space-between'}>
                        {autoAssign && propertyId && (
                            <Col span={24}>
                                <AutoAssignerByDivisions
                                    organizationId={organizationId}
                                    propertyId={propertyId}
                                    categoryClassifier={categoryClassifier}
                                    onDivisionsFound={setDivisions}
                                    form={form}
                                />
                            </Col>
                        )}
                        <Col span={11}>
                            <TicketFormItem
                                name={'executor'}
                                rules={validations.executor}
                                label={<LabelWithInfo title={ExecutorExtra} message={ExecutorLabel}/>}
                            >
                                <GraphQlSearchInput
                                    showArrow={false}
                                    disabled={disableUserInteraction}
                                    formatLabel={formatUserFieldLabel}
                                    renderOptions={renderOptionGroups}
                                    search={searchEmployeeUser(organizationId, ({ role }) => (
                                        get(role, 'canBeAssignedAsExecutor', false)
                                    ))}
                                />
                            </TicketFormItem>
                        </Col>
                        <Col span={11}>
                            <TicketFormItem
                                data-cy={'ticket__assignee-item'}
                                name={'assignee'}
                                rules={validations.assignee}
                                label={<LabelWithInfo title={ResponsibleExtra} message={ResponsibleLabel}/>}
                            >
                                <GraphQlSearchInput
                                    formatLabel={formatUserFieldLabel}
                                    showArrow={false}
                                    disabled={disableUserInteraction}
                                    search={searchEmployeeUser(organizationId, ({ role }) => (
                                        get(role, 'canBeAssignedAsResponsible', false)
                                    ))}
                                />
                            </TicketFormItem>
                        </Col>
                    </Row>
                </Col>
            </Row>
        </Col>
    )
}