lodash#max TypeScript Examples

The following examples show how to use lodash#max. 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: analysis-utils.ts    From prism-frontend with MIT License 7 votes vote down vote up
/**
 * Creates Analysis result legend based on data returned from API.
 *
 * The equal interval method takes the maximum values minus the minimum
 * and divides the result by the number of classes, which is the length
 * of colors array.
 *
 * Finally the function calculates the upper end of each class interval
 * and assigns a color.
 *
 * @return LegendDefinition
 */
export function createLegendFromFeatureArray(
  features: Feature[],
  statistic: AggregationOperations,
): LegendDefinition {
  // Extract values based on aggregation operation.
  const stats: number[] = features.map(f =>
    f.properties && f.properties[statistic] ? f.properties[statistic] : 0,
  );

  const maxNum = Math.max(...stats);
  const minNum = Math.min(...stats);

  const colors = ['#fee5d9', '#fcae91', '#fb6a4a', '#de2d26', '#a50f15'];

  const delta = (maxNum - minNum) / colors.length;

  const legend: LegendDefinition = colors.map((color, index) => {
    const breakpoint = Math.ceil(minNum + (index + 1) * delta);

    // Make sure you don't have a value greater than maxNum.
    const value = Math.min(breakpoint, maxNum);

    return { value, color };
  });

  return legend;
}
Example #2
Source File: analysis-utils.ts    From prism-frontend with MIT License 7 votes vote down vote up
operations = {
  min: (data: number[]) => min(data),
  max: (data: number[]) => max(data),
  sum, // sum method directly from lodash
  mean, // mean method directly from lodash
  median: (data: number[]) => {
    // eslint-disable-next-line fp/no-mutating-methods
    const sortedValues = [...data].sort();
    // Odd cases we use the middle value
    if (sortedValues.length % 2 !== 0) {
      return sortedValues[Math.floor(sortedValues.length / 2)];
    }
    // Even cases we average the two middles
    const floor = sortedValues.length / 2 - 1;
    const ceil = sortedValues.length / 2;
    return (sortedValues[floor] + sortedValues[ceil]) / 2;
  },
}
Example #3
Source File: file.utils.ts    From WowUp with GNU General Public License v3.0 7 votes vote down vote up
export async function getLastModifiedFileDate(sourcePath: string): Promise<number> {
  const dirFiles = await readDirRecursive(sourcePath);
  const dates: number[] = [];
  for (const file of dirFiles) {
    const stat = await fsp.stat(file);
    dates.push(stat.mtimeMs);
  }

  const latest = max(dates);
  return latest;
}
Example #4
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
/**
   *  计算树状模式等宽条件下的列宽
   * @returns number
   */
  private getAdaptTreeColWidth(col: Node, colLeafNodes: Node[]): number {
    // tree row width = [config width, canvas / 2]
    const canvasW = this.getCanvasHW().width;
    const rowHeaderWidth = Math.min(canvasW / 2, this.getTreeRowHeaderWidth());
    // calculate col width
    const colSize = Math.max(1, colLeafNodes.length);
    const { cellCfg } = this.cfg;
    return Math.max(
      getCellWidth(cellCfg, this.getColLabelLength(col)),
      (canvasW - rowHeaderWidth) / colSize,
    );
  }
Example #5
Source File: turn.ts    From fishbowl with MIT License 6 votes vote down vote up
export function drawableCards(
  turns: CurrentGameSubscription["games"][0]["turns"],
  cards: CurrentGameSubscription["games"][0]["cards"]
) {
  const allCompletedCardIds = flatMap(turns, (turn) => turn.completed_card_ids)

  const maxCount = max(values(countBy(allCompletedCardIds)))

  let completedCardIdsForRound = filter(
    groupBy(allCompletedCardIds),
    (arr) => arr.length === maxCount
  ).map((arr) => arr[0])

  const remainingIdsForRound = difference(
    cards.map((card) => card.id),
    completedCardIdsForRound
  )

  if (remainingIdsForRound.length === 0) {
    return cards
  } else {
    return filter(cards, (card) => remainingIdsForRound.includes(card.id))
  }
}
Example #6
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
setConditionShowIndex = <T extends ConditionType>(
  conditions: Array<ICondition<T>>,
  key: string,
  show: boolean,
) => {
  const showIndexArr = map(conditions, 'showIndex');
  const maxShowIndex = max(showIndexArr) as number;
  return map(conditions, (item) => {
    return {
      ...item,
      showIndex: key === item.key ? (show ? (maxShowIndex || 0) + 1 : 0) : item.showIndex,
    };
  });
}
Example #7
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getInitConditions = <T extends ConditionType>(conditions: Array<ICondition<T>>, valueMap: Obj) => {
  const showIndexArr = map(conditions, 'showIndex');
  const maxShowIndex = max(showIndexArr) as number;
  let curMax = maxShowIndex;
  const reConditions = map(conditions, (item) => {
    const curValue = valueMap[item.key];
    // 有值默认展示
    if ((!has(item, 'showIndex') && curValue !== undefined) || (isArray(curValue) && !isEmpty(curValue))) {
      curMax += 1;
      return { ...item, showIndex: curMax };
    }
    return { ...item };
  });
  return reConditions;
}
Example #8
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
/**
   * 计算树状结构行头宽度
   * @returns number
   */
  private getTreeRowHeaderWidth(): number {
    const { rows, dataSet, rowCfg, treeRowsWidth } = this.cfg;
    // user drag happened
    if (rowCfg?.treeRowsWidth) {
      return rowCfg?.treeRowsWidth;
    }

    // + province/city/level
    const treeHeaderLabel = rows
      .map((key: string): string => dataSet.getFieldName(key))
      .join('/');
    const { bolderText: cornerCellTextStyle, icon: cornerIconStyle } =
      this.spreadsheet.theme.cornerCell;
    // 初始化角头时,保证其在树形模式下不换行,给与两个icon的宽度空余(tree icon 和 action icon),减少复杂的 action icon 判断
    const maxLabelWidth =
      measureTextWidth(treeHeaderLabel, cornerCellTextStyle) +
      cornerIconStyle.size * 2 +
      cornerIconStyle.margin?.left +
      cornerIconStyle.margin?.right +
      this.rowCellTheme.padding?.left +
      this.rowCellTheme.padding?.right;

    return Math.max(treeRowsWidth, maxLabelWidth);
  }
Example #9
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
/**
   *  计算平铺模式等宽条件下的列宽
   * @returns number
   */
  private getAdaptGridColWidth(colLeafNodes: Node[], rowHeaderWidth?: number) {
    const { rows, cellCfg } = this.cfg;
    const rowHeaderColSize = rows.length;
    const colHeaderColSize = colLeafNodes.length;
    const canvasW = this.getCanvasHW().width;
    const size = Math.max(1, rowHeaderColSize + colHeaderColSize);
    if (!rowHeaderWidth) {
      // canvasW / (rowHeader's col size + colHeader's col size) = [celCfg.width, canvasW]
      return Math.max(getCellWidth(cellCfg), canvasW / size);
    }
    // (canvasW - rowHeaderW) / (colHeader's col size) = [celCfg.width, canvasW]
    return Math.max(
      getCellWidth(cellCfg),
      (canvasW - rowHeaderWidth) / colHeaderColSize,
    );
  }
Example #10
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
private getColLabelLength(col: Node) {
    // 如果 label 字段形如 "["xx","xxx"]",直接获取其长度
    const labels = safeJsonParse(col?.value);
    if (isArray(labels)) {
      return labels.length;
    }

    // 否则动态采样前50条数据,如果数据value是数组类型,获取其长度
    const { dataSet } = this.cfg;
    const multiData = dataSet.getMultiData(
      col.query,
      col.isTotals || col.isTotalMeasure,
    );
    // 采样前50,根据指标个数获取单元格列宽
    const demoData = multiData?.slice(0, 50) ?? [];
    const lengths = [];
    forEach(demoData, (value) => {
      forIn(value, (v: MultiData) => {
        if (isObject(v) && v?.values) {
          lengths.push(size(v?.values[0]));
        }
      });
    });
    return max(lengths) || 1;
  }
Example #11
Source File: base-data-set.ts    From S2 with MIT License 6 votes vote down vote up
public getValueRangeByField(field: string): ValueRange {
    const cacheRange = getValueRangeState(this.spreadsheet, field);
    if (cacheRange) {
      return cacheRange;
    }
    const fieldValues = compact(
      map(this.originData, (item) => {
        const value = item[field] as string;
        return isNil(value) ? null : Number.parseFloat(value);
      }),
    );
    const range = {
      maxValue: max(fieldValues),
      minValue: min(fieldValues),
    };
    setValueRangeState(this.spreadsheet, {
      [field]: range,
    });
    return range;
  }
Example #12
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
BarContentRender = (props: IBarProps) => {
  const { task, isHover } = props;
  const barRef = React.useRef<HTMLDivElement>(null);
  const nameRef = React.useRef<HTMLDivElement>(null);
  const [linearPercent, setLinearPercent] = React.useState(100);

  const barWidth = barRef.current?.offsetWidth || 40;
  const nameWidth = nameRef.current?.offsetWidth || 40;
  React.useLayoutEffect(() => {
    setLinearPercent(((barWidth - 8) / nameWidth) * 100);
  }, [barWidth, nameWidth]);

  return (
    <div className={'relative h-full'} ref={barRef}>
      <div className={`flex items-center h-full w-full`}>
        <div style={{ flex: `0 0 ${max([barWidth, nameWidth])}px` }} className={` ml-2 `}>
          <span
            ref={nameRef}
            className="text-xs whitespace-nowrap"
            style={{
              padding: '14px 0',
              WebkitMaskImage: `linear-gradient(90deg, rgba(48,38,71,0.80) ${linearPercent}%, rgba(48,38,71,0.32) ${linearPercent}%)`,
            }}
          >
            {task.name}
          </span>
        </div>
        <div className={` ml-1 whitespace-nowrap text-sub text-xs ${isHover ? 'visible' : 'invisible'}`}>
          {moment(task.start).format('MM-DD')} ~ {moment(task.end).format('MM-DD')}
        </div>
      </div>
    </div>
  );
}
Example #13
Source File: edit-project.component.ts    From data-annotator-for-machine-learning with Apache License 2.0 6 votes vote down vote up
minUpdate(e) {
    if (this.labelType === 'numericLabel' && this.isMultipleLabel) {
      this.checkBoth();
    } else {
      if (e != null && Number(this.msg.max) >= Number(e) && Number(this.msg.max) !== Number(this.msg.min)) {
        this.sizeError = false;
      } else {
        this.sizeError = true;
      }
    }
  }
Example #14
Source File: edit-project.component.ts    From data-annotator-for-machine-learning with Apache License 2.0 6 votes vote down vote up
maxUpdate(e) {
    if (this.labelType === 'numericLabel' && this.isMultipleLabel) {
      this.checkBoth();
    } else {
      if (e != null && Number(e) >= Number(this.msg.min) && Number(this.msg.max) !== Number(this.msg.min)) {
        this.sizeError = false;
      } else {
        this.sizeError = true;
      }
    }
  }
Example #15
Source File: github_analyzer.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
createWorkflowReport(workflowName: string, workflow: WorkflowRunsItem, jobs: JobsItem, tagMap: RepositoryTagMap): WorkflowReport {
    const { workflowId, buildNumber, workflowRunId }
      = this.createWorkflowParams(workflowName, workflow)

    const jobReports: JobReport[] = jobs.map((job) => {
      const stepReports: StepReport[] = job.steps!.map((step) => {
        const startedAt = new Date(step.started_at!)
        const completedAt = new Date(step.completed_at!)
        // step
        return {
          name: step.name,
          status: this.normalizeStatus(step.conclusion),
          number: step.number,
          startedAt,
          completedAt,
          stepDurationSec: diffSec(startedAt, completedAt)
        }
      })

      const startedAt = new Date(job.started_at)
      const completedAt = new Date(job.completed_at!)
      // job
      return {
        workflowRunId: workflowRunId,
        buildNumber: buildNumber, // Github Actions job does not have buildNumber
        jobId: String(job.id),
        jobName: job.name,
        status: this.normalizeStatus(job.conclusion),
        startedAt,
        completedAt,
        jobDurationSec: diffSec(startedAt, completedAt),
        sumStepsDurationSec: sumBy(stepReports, 'stepDurationSec'),
        steps: stepReports,
        url: job.html_url ?? '',
        executorClass: '',
        executorType: '',
        executorName: job.runner_name ?? '',
      }
    })

    const createdAt = new Date(workflow.created_at)
    const startedAt = min(jobReports.map((job) => job.startedAt )) || createdAt
    const completedAt = max(jobReports.map((job) => job.completedAt )) || createdAt
    const status = this.normalizeStatus(workflow.conclusion as unknown as string)
    // workflow
    return {
      service: 'github',
      workflowId,
      buildNumber,
      workflowRunId,
      workflowName,
      createdAt,
      trigger: workflow.event,
      status,
      repository: workflow.repository.full_name,
      headSha: workflow.head_sha,
      branch: workflow.head_branch ?? '',
      tag: tagMap.get(workflow.head_sha) ?? '',
      jobs: jobReports,
      startedAt,
      completedAt,
      workflowDurationSec: diffSec(startedAt, completedAt),
      sumJobsDurationSec: sumBy(jobReports, 'sumStepsDurationSec'),
      successCount: (status === 'SUCCESS') ? 1 : 0,
      parameters: [],
      queuedDurationSec: diffSec(createdAt, startedAt),
      commitMessage: workflow.head_commit?.message ?? '',
      actor: workflow.head_commit?.author?.name ?? '',
      url: workflow.html_url,
    }
  }
Example #16
Source File: date-helper.ts    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
ganttDateRange = (tasks: Task[], viewMode: ViewMode) => {
  let newStartDate: Date = tasks[0].start || 0;
  let newEndDate: Date = tasks[0].start || 0;

  const timeArr = compact(flatten(tasks.map((item) => [item.start, item.end])));

  const minTime = min(timeArr);
  const maxTime = max(timeArr);
  newStartDate = minTime && new Date(minTime);
  newEndDate = maxTime && new Date(maxTime);

  if (!newStartDate) {
    newStartDate = new Date(moment().subtract(15, 'days'));
  }
  if (!newEndDate || newEndDate.getTime() === newStartDate.getTime()) {
    newEndDate = new Date(moment(newStartDate).subtract(-30, 'days'));
  }

  // start time is bigger then end time
  if (newStartDate.getTime() > newEndDate.getTime()) {
    [newStartDate, newEndDate] = [newEndDate, newStartDate];
  }

  switch (viewMode) {
    case ViewMode.Month:
      newStartDate = addToDate(newStartDate, -1, 'month');
      newStartDate = startOfDate(newStartDate, 'month');
      newEndDate = addToDate(newEndDate, 1, 'year');
      newEndDate = startOfDate(newEndDate, 'year');
      break;
    case ViewMode.Week:
      newStartDate = startOfDate(newStartDate, 'day');
      newEndDate = startOfDate(newEndDate, 'day');
      newStartDate = addToDate(getMonday(newStartDate), -7, 'day');
      newEndDate = addToDate(newEndDate, 1.5, 'month');
      break;
    case ViewMode.Day:
      newStartDate = startOfDate(newStartDate, 'day');
      newEndDate = startOfDate(newEndDate, 'day');
      newStartDate = addToDate(newStartDate, -1, 'day');
      newEndDate = addToDate(newEndDate, 19, 'day');
      break;
    case ViewMode.QuarterDay:
      newStartDate = startOfDate(newStartDate, 'day');
      newEndDate = startOfDate(newEndDate, 'day');
      newStartDate = addToDate(newStartDate, -1, 'day');
      newEndDate = addToDate(newEndDate, 66, 'hour'); // 24(1 day)*3 - 6
      break;
    case ViewMode.HalfDay:
      newStartDate = startOfDate(newStartDate, 'day');
      newEndDate = startOfDate(newEndDate, 'day');
      newStartDate = addToDate(newStartDate, -1, 'day');
      newEndDate = addToDate(newEndDate, 108, 'hour'); // 24(1 day)*5 - 12
      break;
    default:
      break;
  }
  return [newStartDate, newEndDate];
}
Example #17
Source File: task-gantt.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
TaskGantt: React.FC<TaskGanttProps> = ({ gridProps, calendarProps, barProps, BarContentRender }) => {
  const ganttSVGRef = useRef<SVGSVGElement>(null);
  const newBarProps = { ...barProps, svg: ganttSVGRef };
  const verticalGanttContainerRef = useRef<HTMLDivElement>(null);
  const offsetWidth = verticalGanttContainerRef?.current?.offsetWidth;
  const [mousePos, setMousePos] = React.useState<null | number[]>(null);
  const onMouseMove = (e: React.MouseEvent) => {
    const gridPos = e.currentTarget.getBoundingClientRect();
    const mouseY = max([e.clientY - gridPos.y, 0]);
    const mouseX = max([e.clientX - gridPos.x]);
    setMousePos([Math.floor(mouseX / gridProps.columnWidth), Math.floor(mouseY / gridProps.rowHeight)]);
  };

  const mouseUnFocus = () => {
    setMousePos(null);
  };

  return (
    <div className={'erda-gantt-vertical-container'} dir="ltr" ref={verticalGanttContainerRef}>
      <Calendar
        {...calendarProps}
        width={max([calendarProps.width, offsetWidth])}
        displayWidth={offsetWidth}
        mousePos={mousePos}
      />
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width={max([gridProps.svgWidth, offsetWidth])}
        height={barProps.rowHeight * barProps.tasks.length}
        fontFamily={barProps.fontFamily}
        style={{ overflow: 'visible' }}
        ref={ganttSVGRef}
      >
        <g onMouseMove={onMouseMove} onMouseLeave={mouseUnFocus}>
          <Grid
            {...gridProps}
            svgWidth={max([gridProps.svgWidth, offsetWidth])}
            displayWidth={offsetWidth}
            onMouseMove={onMouseMove}
            mouseUnFocus={mouseUnFocus}
            mousePos={mousePos}
          />
        </g>
        <TaskGanttContent {...newBarProps} displayWidth={offsetWidth} BarContentRender={BarContentRender} />
      </svg>
    </div>
  );
}
Example #18
Source File: edit-project.component.ts    From data-annotator-for-machine-learning with Apache License 2.0 5 votes vote down vote up
ngOnInit() {
    this.msg = JSON.parse(JSON.stringify(this.msgInEdit));
    const al = this.msg.al;
    this.previousProjectName = this.msg.projectName;
    this.inputProjectName = this.msg.projectName;
    this.inputTaskInstruction = this.msg.taskInstructions;
    this.inputfrequency = al.frequency ? al.frequency : null;
    this.inputTrigger = al.trigger ? al.trigger : null;
    this.labelType = this.msg.labelType;
    this.isMultipleLabel = this.msg.isMultipleLabel;
    this.inputProjectCreator = this.msg.creator;
    this.inputProjectAssignee = this.msg.annotator;
    let flag = [];
    _.cloneDeep(this.msg.annotator).forEach((annotator) => {
      for (let i = 0; i < this.msg.userCompleteCase.length; i++) {
        if (annotator === this.msg.userCompleteCase[i].user) {
          flag.push({
            email: annotator,
            assignedCase: this.msg.userCompleteCase[i].assignedCase,
            completeCase: this.msg.userCompleteCase[i].completeCase,
          });
          break;
        }
      }
    });
    this.assigneeList = flag;
    this.ownerList = JSON.parse(JSON.stringify(this.msg.creator));
    this.assignmentLogicEdit = this.msg.assignmentLogic;
    this.isShowFilename = this.msg.isShowFilename ? 'yes' : 'no';
    this.oldMax = this.msg.max;
    this.oldMin = this.msg.min;
    if (this.labelType === 'numericLabel' && this.isMultipleLabel) {
      let mutilNumerics = [];
      let categoryList = JSON.parse(this.msg.categoryList);
      categoryList.forEach(element => {
        const labels = Object.keys(element);
        const label = labels[0];
        const values = element[label];
        const minVal = values[0];
        const maxVal = values[1];
        mutilNumerics.push(this.formBuilder.group({
          status: 'old', 
          originalLabel: label,
          editLabel: label,
          oldMinMutilVal:  this.formBuilder.control(minVal),
          minMutilVal:  this.formBuilder.control(minVal),
          oldMaxMutilVal:  this.formBuilder.control(maxVal),
          maxMutilVal: this.formBuilder.control(maxVal),
        }));
      });
      this.mutilNumericForm = this.formBuilder.group({
        mutilLabelArray: this.formBuilder.array(mutilNumerics)
      });
    } else {
      this.msg.categoryList.split(',').forEach((element) => {
        const flag = { status: 'old', originalLabel: element, editLabel: element };
        this.categoryList.push(flag);
      });
    }
  }
Example #19
Source File: pivot-facet.ts    From S2 with MIT License 5 votes vote down vote up
/**
   * 计算 compact 模式下 node 宽度
   *
   * |   fieldName  |
   *  _______________
   * | label - icon  | <- node
   * | label - icon  |
   * | label - icon  |
   *
   * @param node 目标节点
   * @returns 宽度
   */
  private getCompactGridRowWidth(node: Node): number {
    const { dataSet, spreadsheet } = this.cfg;
    const {
      bolderText: rowTextStyle,
      icon: rowIconStyle,
      cell: rowCellStyle,
    } = spreadsheet.theme.rowCell;
    const {
      bolderText: cornerTextStyle,
      icon: cornerIconStyle,
      cell: cornerCellStyle,
    } = spreadsheet.theme.cornerCell;
    const { field, isLeaf } = node;

    // calc rowNodeWitdh
    const rowIconWidth = this.getExpectedCellIconWidth(
      CellTypes.ROW_CELL,
      !spreadsheet.isValueInCols() &&
        isLeaf &&
        spreadsheet.options.showDefaultHeaderActionIcon,
      rowIconStyle,
    );
    const allLabels = dataSet
      .getDimensionValues(field)
      ?.slice(0, 50)
      .map(
        (dimValue) =>
          this.spreadsheet.dataSet.getFieldFormatter(field)?.(dimValue) ??
          dimValue,
      );
    const maxLabel = maxBy(allLabels, (label) => `${label}`.length);
    const rowNodeWidth =
      measureTextWidth(maxLabel, rowTextStyle) +
      rowIconWidth +
      rowCellStyle.padding.left +
      rowCellStyle.padding.right;

    // calc corner fieldNameNodeWidth
    const fieldName = dataSet.getFieldName(field);
    const cornerIconWidth = this.getExpectedCellIconWidth(
      CellTypes.CORNER_CELL,
      false,
      cornerIconStyle,
    );
    const fieldNameNodeWidth =
      measureTextWidth(fieldName, cornerTextStyle) +
      cornerIconWidth +
      cornerCellStyle.padding.left +
      cornerCellStyle.padding.right;

    DebuggerUtil.getInstance().logger(
      'Max Label In Row:',
      field,
      rowNodeWidth > fieldNameNodeWidth ? maxLabel : fieldName,
    );

    // return max
    return Math.max(rowNodeWidth, fieldNameNodeWidth);
  }
Example #20
Source File: circleci_runner_v2.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
private setRepoLastRun(reponame: string, pipelines: Pipeline[]) {
    const maxBuildNumber = max(pipelines.map((pipeline) => pipeline.number))
    if (maxBuildNumber) {
      this.store?.setLastRun(reponame, maxBuildNumber)
    }
  }
Example #21
Source File: circleci_client.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
// https://circleci.com/api/v1.1/project/:vcs-type/:username/:project?circle-token=:token&limit=20&offset=5&filter=completed
  async fetchWorkflowRuns(owner: string, repo: string, vcsType: string, lastRunId?: number) {
    const limit = (this.options.debug) ? DEBUG_PER_PAGE : 100
    let recentBuilds = [] as RecentBuildResponse[]
    for (let index = 0; index < FETCH_RECENT_BUILD_API_NUM; index++) {
      const res = await this.axios.get( `project/${vcsType}/${owner}/${repo}`, {
        params: {
          // API default is 30 and max is 100
          // ref: https://circleci.com/docs/api/#recent-builds-for-a-single-project
          limit: limit,
          // limit: 3,
          offset: index * limit,
          // filter: "completed"
          shallow: true,
        }
      })
      recentBuilds.push(...res.data)
    }
    recentBuilds = (lastRunId)
      ? recentBuilds.filter((build) => build.build_num > lastRunId)
      : recentBuilds

    // Add dummy workflow data if job is not belong to workflow
    for (const build of recentBuilds) {
      if (!build.workflows) {
        build.workflows = this.createDefaultWorkflow(build)
      }
    }

    const groupedBuilds = groupBy(recentBuilds.map((build) => {
      return {
        workflow_name: build.workflows.workflow_name,
        workflow_id: build.workflows.workflow_id,
        reponame: build.reponame,
        username: build.username,
        vcs_type: vcsType,
        build_num: build.build_num,
        lifecycle: build.lifecycle,
      }
    }), 'workflow_id')
    const workflowRuns: WorkflowRun[] = Object.values(groupedBuilds).map((builds) => {
      const build = builds[0]
      const build_nums = builds.map((build) => build.build_num)
      return {
        workflow_id: build.workflow_id,
        workflow_name: build.workflow_name,
        reponame: build.reponame,
        username: build.username,
        vcs_type: build.vcs_type,
        build_nums,
        lifecycles: builds.map((build) => build.lifecycle),
        last_build_num: max(build_nums)!,
      }
    })

    return this.filterWorkflowRuns(workflowRuns)
  }
Example #22
Source File: time-series.spec.ts    From aqualink-app with MIT License 4 votes vote down vote up
timeSeriesTests = () => {
  const testService = TestService.getInstance();
  let app: INestApplication;
  let surveyPointDataRange: StringDateRange = [
    new Date(0).toISOString(),
    new Date().toISOString(),
  ];
  let siteDataRange: StringDateRange = [
    new Date(0).toISOString(),
    new Date().toISOString(),
  ];

  beforeAll(async () => {
    app = await testService.getApp();
  });

  it('GET /sites/:siteId/site-survey-points/:surveyPointId/range fetch range of poi data', async () => {
    const rsp = await request(app.getHttpServer()).get(
      `/time-series/sites/${athensSite.id}/site-survey-points/${athensSurveyPointPiraeus.id}/range`,
    );

    expect(rsp.status).toBe(200);
    const metrics = union(hoboMetrics, NOAAMetrics);
    metrics.forEach((metric) => {
      expect(rsp.body).toHaveProperty(metric);
    });
    hoboMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.HOBO);
      expect(rsp.body[metric][SourceType.HOBO].data.length).toBe(1);
      const { minDate, maxDate } = rsp.body[metric][SourceType.HOBO].data[0];
      const [startDate, endDate] = surveyPointDataRange;
      surveyPointDataRange = [
        min([minDate, startDate]),
        max([maxDate, endDate]),
      ];
    });
    NOAAMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.NOAA);
      expect(rsp.body[metric][SourceType.NOAA].data.length).toBe(1);
      const { minDate, maxDate } = rsp.body[metric][SourceType.NOAA].data[0];
      const [startDate, endDate] = surveyPointDataRange;
      surveyPointDataRange = [
        min([minDate, startDate]),
        max([maxDate, endDate]),
      ];
    });
  });

  it('GET /sites/:id/range fetch range of site data', async () => {
    const rsp = await request(app.getHttpServer()).get(
      `/time-series/sites/${californiaSite.id}/range`,
    );

    expect(rsp.status).toBe(200);
    const metrics = union(NOAAMetrics, spotterMetrics);
    metrics.forEach((metric) => {
      expect(rsp.body).toHaveProperty(metric);
    });
    NOAAMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.NOAA);
      expect(rsp.body[metric][SourceType.NOAA].data.length).toBe(1);
      const { minDate, maxDate } = rsp.body[metric][SourceType.NOAA].data[0];
      const [startDate, endDate] = siteDataRange;
      siteDataRange = [min([minDate, startDate]), max([maxDate, endDate])];
    });
    spotterMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.SPOTTER);
      expect(rsp.body[metric][SourceType.SPOTTER].data.length).toBe(1);
      const { minDate, maxDate } = rsp.body[metric][SourceType.SPOTTER].data[0];
      const [startDate, endDate] = siteDataRange;
      siteDataRange = [min([minDate, startDate]), max([maxDate, endDate])];
    });
  });

  it('GET /sites/:siteId/site-survey-points/:surveyPointId fetch poi data', async () => {
    const [startDate, endDate] = surveyPointDataRange;
    const rsp = await request(app.getHttpServer())
      .get(
        `/time-series/sites/${athensSite.id}/site-survey-points/${athensSurveyPointPiraeus.id}`,
      )
      .query({
        // Increase the search window to combat precision issues with the dates
        start: moment(startDate).subtract(1, 'minute').toISOString(),
        end: moment(endDate).add(1, 'day').toISOString(),
        metrics: hoboMetrics.concat(NOAAMetrics),
        hourly: false,
      });

    expect(rsp.status).toBe(200);
    const metrics = union(hoboMetrics, NOAAMetrics);
    metrics.forEach((metric) => {
      expect(rsp.body).toHaveProperty(metric);
    });
    hoboMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.HOBO);
      expect(rsp.body[metric][SourceType.HOBO].data.length).toBe(10);
    });
    NOAAMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.NOAA);
      expect(rsp.body[metric][SourceType.NOAA].data.length).toBe(10);
    });
  });

  it('GET /sites/:siteId fetch site data', async () => {
    const [startDate, endDate] = siteDataRange;
    const rsp = await request(app.getHttpServer())
      .get(`/time-series/sites/${californiaSite.id}`)
      .query({
        // Increase the search window to combat precision issues with the dates
        start: moment(startDate).subtract(1, 'minute').toISOString(),
        end: moment(endDate).add(1, 'day').toISOString(),
        metrics: spotterMetrics.concat(NOAAMetrics),
        hourly: false,
      });

    expect(rsp.status).toBe(200);
    const metrics = union(NOAAMetrics, spotterMetrics);
    metrics.forEach((metric) => {
      expect(rsp.body).toHaveProperty(metric);
    });
    NOAAMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.NOAA);
      expect(rsp.body[metric][SourceType.NOAA].data.length).toBe(10);
    });
    spotterMetrics.forEach((metric) => {
      expect(rsp.body[metric]).toHaveProperty(SourceType.SPOTTER);
      expect(rsp.body[metric][SourceType.SPOTTER].data.length).toBe(10);
    });
  });
}
Example #23
Source File: philosophers.tsx    From S2 with MIT License 4 votes vote down vote up
fetch(
  'https://gw.alipayobjects.com/os/bmw-prod/24cac0f7-70f0-4131-be61-df11da3ca921.json',
)
  .then((res) => res.json())
  .then((data) => {
    const weights = data.map((item) => item.weight);
    const maxWeight = max(weights);
    const minWeight = min(weights);
    const weightSpan = maxWeight - minWeight;

    const PaletteLegend = () => (
      <div className="legend">
        <div className="legend-limit">{minWeight.toFixed(2)}</div>
        {PALETTE_COLORS.map((color, index) => (
          <span
            key={index}
            className="legend-color"
            style={{ background: color }}
          />
        ))}
        <div className="legend-limit">{maxWeight.toFixed(2)}</div>
      </div>
    );

    const getFormatter = (val) => {
      if (val < 0) {
        return `公元前${replace(val, '-', '')}年`;
      } else {
        return `${val}年`;
      }
    };

    const s2DataConfig = {
      fields: {
        rows: ['country', 'name', 'start', 'end', 'points', 'word'],
        columns: [],
        values: ['weight'],
      },
      meta: [
        {
          field: 'word',
          name: '关键词',
        },
        {
          field: 'points',
          name: '观点',
        },
        {
          field: 'name',
          name: '姓名',
        },
        {
          field: 'country',
          name: '国家',
        },
        {
          field: 'start',
          name: '出生',
          formatter: getFormatter,
        },
        {
          field: 'end',
          name: '逝世',
          formatter: getFormatter,
        },
        {
          field: 'weight',
          name: '权重',
          formatter: (val) => val.toFixed(2),
        },
      ],
      data,
    };
    const TooltipContent = (props) => {
      const { rowQuery, fieldValue } = props;
      const { name, country, start, end, points } = rowQuery;
      const ponitsLines = points.split('&');
      return (
        <div className="antv-s2-tooltip-container">
          <div className="antv-s2-tooltip-head-info-list">
            <div>姓名:{name}</div>
            <div>国家:{country}</div>
            <div>出生:{getFormatter(start)}</div>
            <div>逝世:{getFormatter(end)}</div>
            {ponitsLines.length > 1 ? (
              <div>
                观点:
                {ponitsLines.map((point, index) => (
                  <div>
                    {index + 1}: {point}
                  </div>
                ))}
              </div>
            ) : (
              <div>观点: {ponitsLines[0]}</div>
            )}
          </div>
          <div className="antv-s2-tooltip-divider"></div>
          <div className="antv-s2-tooltip-detail-list">
            <div className="antv-s2-tooltip-detail-item">
              <span className="antv-s2-tooltip-detail-item-key">权重</span>
              <span className="antv-s2-tooltip-detail-item-val">
                {fieldValue}
              </span>
            </div>
          </div>
        </div>
      );
    };
    const s2Options = {
      width: '',
      height: 400,
      conditions: {
        text: [
          {
            field: 'weight',
            mapping(value) {
              if (value >= 20) {
                return {
                  fill: '#fff',
                };
              }
            },
          },
        ],
        background: [
          {
            field: 'weight',
            mapping(value) {
              let backgroundColor;
              const colorIndex =
                Math.floor(
                  (((value - minWeight) / weightSpan) * 100) /
                    PALETTE_COLORS.length,
                ) - 1;
              if (colorIndex <= 0) {
                backgroundColor = PALETTE_COLORS[0];
              } else if (colorIndex >= PALETTE_COLORS.length) {
                backgroundColor = PALETTE_COLORS[PALETTE_COLORS.length - 1];
              } else {
                backgroundColor = PALETTE_COLORS[colorIndex];
              }

              return {
                fill: backgroundColor,
              };
            },
          },
        ],
      },
      interaction: {
        selectedCellsSpotlight: false,
        hoverHighlight: false,
      },
    };

    const onDataCellMouseUp = (value) => {
      const viewMeta = value?.viewMeta;
      if (!viewMeta) {
        return;
      }

      const position = {
        x: value.event.clientX,
        y: value.event.clientY,
      };
      viewMeta.spreadsheet.tooltip.show({
        position,
        content: TooltipContent(viewMeta),
      });
    };

    ReactDOM.render(
      <SheetComponent
        dataCfg={s2DataConfig}
        options={s2Options}
        adaptive={true}
        header={{
          title: '哲学家的观点',
          extra: [<PaletteLegend />],
        }}
        onDataCellMouseUp={onDataCellMouseUp}
      />,

      document.getElementById('container'),
    );
  });
Example #24
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
TagsRow = ({
  labels: propsLabels,
  showCount = 2,
  containerClassName = '',
  labelsClassName = '',
  size = 'small',
  colorMap,
  onDelete,
  onAdd,
}: IProps) => {
  const labels = propsLabels ? (Array.isArray(propsLabels) ? propsLabels : [propsLabels]) : [];
  const showMore = labels.length > showCount;
  const showGroup = some(labels, (l) => has(l, 'group'));

  const [labelWidth, setLabelWidth] = React.useState<string | number>('auto');

  const countLabelWidth = () => {
    const labelEles = document.querySelectorAll('.tag-group-name');
    const maxWidth: number = max(map(labelEles, (ele: HTMLSpanElement) => ele.offsetWidth));
    setLabelWidth(Math.min(maxWidth, MAX_LABEL_WIDTH) + 8);
  };

  const fullTags = () => {
    if (showGroup) {
      return (
        <div>
          {map(groupBy(labels, 'group'), (groupItem, gKey) => (
            <div key={gKey} className="tag-group-container mb-2">
              <span className="tag-group-name" style={{ width: labelWidth }}>{`${gKey} : `}</span>
              <span className="flex-1 overflow-auto">
                {groupItem.map((item) => (
                  <TagItem colorMap={colorMap} key={item.label} label={item} onDelete={onDelete} size={size} />
                ))}
              </span>
            </div>
          ))}
        </div>
      );
    }
    return labels.map((l) => <TagItem colorMap={colorMap} key={l.label} label={l} onDelete={onDelete} size={size} />);
  };

  const oneAndMoreTag = (
    <React.Fragment>
      {labels.slice(0, showCount).map((l) => (
        <TagItem colorMap={colorMap} key={l.label} label={l} maxWidth={100} onDelete={onDelete} size={size} />
      ))}
      {showMore ? (
        <Tooltip
          onVisibleChange={(vis) => {
            if (vis && labelWidth === 'auto') {
              countLabelWidth();
            }
          }}
          title={
            <div onClick={(e) => e.stopPropagation()} className="tags-container ">
              {fullTags()}
            </div>
          }
          placement="right"
          overlayClassName="tags-row-tooltip"
        >
          <ErdaIcon className={`twt-tag-ellipsis ${size}`} type="more" color="currentColor" />
        </Tooltip>
      ) : null}
    </React.Fragment>
  );

  return (
    <div
      className={`tags-container flex items-center justify-start overflow-hidden ${containerClassName}`}
      onClick={(e) => e.stopPropagation()}
    >
      <span className={`tags-box flex items-center ${labelsClassName}`}>{oneAndMoreTag}</span>
      {onAdd ? (
        <ErdaIcon
          className={`tags-add ${size} ml-2 text-xs leading-6 cursor-pointer`}
          type="plus"
          color="currentColor"
          onClick={onAdd}
        />
      ) : null}
    </div>
  );
}
Example #25
Source File: calendar.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
Calendar: React.FC<CalendarProps> = React.memo(
  ({
    dateSetup,
    locale,
    viewMode,
    rtl,
    width,
    height,
    columnWidth,
    horizontalRange,
    fontFamily,
    fontSize,
    highlightRange,
    displayWidth,
    scrollX,
    setScrollX,
    svgWidth,
    mousePos,
  }) => {
    const today = new Date();
    const getCalendarValuesForMonth = () => {
      const topValues: ReactChild[] = [];
      const bottomValues: ReactChild[] = [];
      const topDefaultHeight = height * 0.5;
      for (let i = 0; i < dateSetup.dates.length; i++) {
        const date = dateSetup.dates[i];
        const bottomValue = getLocaleMonth(date, locale);
        bottomValues.push(
          <text
            key={bottomValue + date.getFullYear()}
            y={height * 0.8}
            x={columnWidth * i + columnWidth * 0.5}
            className={'erda-gantt-calendar-bottom-text'}
          >
            {bottomValue}
          </text>,
        );
        if (i === 0 || date.getFullYear() !== dateSetup.dates[i - 1].getFullYear()) {
          const topValue = date.getFullYear().toString();
          let xText: number;
          if (rtl) {
            xText = (6 + i + date.getMonth() + 1) * columnWidth;
          } else {
            xText = (6 + i - date.getMonth()) * columnWidth;
          }
          topValues.push(
            <TopPartOfCalendar
              key={topValue}
              value={topValue}
              x1Line={columnWidth * i}
              y1Line={0}
              y2Line={topDefaultHeight}
              xText={xText}
              yText={topDefaultHeight * 0.9}
            />,
          );
        }
      }

      return [topValues, bottomValues];
    };

    const getCalendarValuesForWeek = () => {
      const topValues: ReactChild[] = [];
      const bottomValues: ReactChild[] = [];
      let weeksCount = 1;
      const topDefaultHeight = height * 0.5;
      const { dates } = dateSetup;
      for (let i = dates.length - 1; i >= 0; i--) {
        const date = dates[i];
        let topValue = '';
        if (i === 0 || date.getMonth() !== dates[i - 1].getMonth()) {
          // top
          topValue = `${getLocaleMonth(date, locale)}, ${date.getFullYear()}`;
        }
        // bottom
        const bottomValue = `W${getWeekNumberISO8601(date)}`;

        bottomValues.push(
          <text
            key={date.getTime()}
            y={height * 0.8}
            x={columnWidth * (i + +rtl)}
            className={'erda-gantt-calendar-bottom-text'}
          >
            {bottomValue}
          </text>,
        );

        if (topValue) {
          // if last day is new month
          if (i !== dates.length - 1) {
            topValues.push(
              <TopPartOfCalendar
                key={topValue}
                value={topValue}
                x1Line={columnWidth * i + weeksCount * columnWidth}
                y1Line={0}
                y2Line={topDefaultHeight}
                xText={columnWidth * i + columnWidth * weeksCount * 0.5}
                yText={topDefaultHeight * 0.9}
              />,
            );
          }
          weeksCount = 0;
        }
        weeksCount++;
      }
      return [topValues, bottomValues];
    };
    const reHighlightRange = {
      ...highlightRange,
      ...(highlightRange?.id && !highlightRange.start && !highlightRange.end ? { x1: -1, x2: -1 } : {}),
    };
    const HoverBar = ({ style }: { style: Obj }) =>
      highlightRange ? (
        <div
          className="absolute rounded bg-black-06"
          style={{
            width: Math.abs(reHighlightRange.x2 - reHighlightRange.x1),
            height: 40,
            left: min([reHighlightRange.x1, reHighlightRange.x2]),
            top: 26,
            ...style,
          }}
        />
      ) : null;

    const HoverTime = ({ style }: { style: Obj }) =>
      mousePos ? (
        <div
          className="absolute rounded bg-black-06"
          style={{
            width: columnWidth,
            height: 40,
            left: mousePos[0] * columnWidth,
            top: 26,
            ...style,
          }}
        />
      ) : null;

    const onChangeScrollX = (direction: number) => {
      const moveLen = Math.floor(displayWidth / columnWidth) - 4; // less then display count;
      const moveX = moveLen > 0 ? moveLen * columnWidth : columnWidth;
      if (direction === -1) {
        setScrollX((prevX) => (moveX >= prevX ? -1 : prevX - moveX));
      } else if (direction === 1) {
        setScrollX((prevX) => (moveX + prevX + displayWidth >= svgWidth ? svgWidth - displayWidth : prevX + moveX));
      }
    };

    const getCalendarValuesForDay = () => {
      let bottomValues: React.ReactNode = null;
      const dates = dateSetup.dates.slice(...horizontalRange);
      const dateInWeeks = [];
      // append date when screen have more space
      if (!dates.length) return null;
      let appendDateLength = Math.max(0, horizontalRange[1] - horizontalRange[0] - dates.length);
      while (appendDateLength-- > 0) {
        const lastDayInLastWeek = dates[dates.length - 1];
        dates.push(addToDate(lastDayInLastWeek, 1, 'day'));
      }
      const firstDay = dates[0];

      const firstDayInWeek = firstDay.getDay();
      // use Monday as first day of week

      const firstWeek = dates.splice(0, firstDayInWeek === 0 ? 1 : 7 - firstDayInWeek + 1);
      while (firstWeek.length < 7) {
        const firstDayInFirstWeek = firstWeek[0];
        firstWeek.unshift(addToDate(firstDayInFirstWeek, -1, 'day'));
      }
      dateInWeeks.push(firstWeek);
      while (dates.length) {
        dateInWeeks.push(dates.splice(0, 7));
      }
      const lastWeek = dateInWeeks[dateInWeeks.length - 1];
      while (lastWeek.length < 7) {
        const lastDayInLastWeek = lastWeek[lastWeek.length - 1];
        lastWeek.push(addToDate(lastDayInLastWeek, 1, 'day'));
      }

      const offsetX = (firstDayInWeek ? firstDayInWeek - 1 : 6) * columnWidth;
      bottomValues = (
        <div
          className="flex h-full w-full erda-gantt-calendar-header-container"
          style={{ transform: `translateX(${-offsetX}px)` }}
        >
          {<HoverBar style={{ transform: `translateX(${offsetX}px)` }} />}
          {<HoverTime style={{ transform: `translateX(${offsetX}px)` }} />}
          {flatten(dateInWeeks).map((day, idx) => {
            const mark =
              reHighlightRange?.x1 === columnWidth * idx - offsetX ||
              reHighlightRange?.x2 === columnWidth * (idx + 1) - offsetX;
            const cls = `${
              mark
                ? 'calendar-highlight-text'
                : `${[0, 6].includes(day.getDay()) ? 'calendar-disabled-text' : 'calendar-normal-text'}`
            }`;
            const isStartPos = columnWidth * idx - offsetX === 0;
            const isToday = moment(day).isSame(today, 'day');
            return (
              <div
                key={day.getTime()}
                style={{
                  width: columnWidth,
                  height: 40,
                  top: 28,
                  left: columnWidth * idx,
                }}
                className={`absolute flex flex-col items-center text-xs justify-center ${cls} ${
                  isToday ? 'text-red' : ''
                }`}
              >
                <span>{Days[day.getDay()]}</span>
                <span>{day.getDate()}</span>
                {isStartPos || day.getDate() === 1 ? (
                  <div className="absolute text-default-8 font-medium " style={{ top: -16 }}>
                    {Months[day.getMonth()]}
                  </div>
                ) : null}
                {isToday ? (
                  <div
                    style={{ left: (columnWidth + 22) / 2, bottom: -14 }}
                    className="absolute erda-gantt-calendar-today flex justify-center"
                  >
                    <div>{i18n.t('dop:Today')}</div>
                  </div>
                ) : null}
              </div>
            );
          })}
          {scrollX > 0 ? (
            <div
              className="flex items-center erda-gantt-calendar-arrow-left"
              onClick={() => onChangeScrollX(-1)}
              style={{ left: offsetX }}
            >
              <ErdaIcon type="zuofan" className="ml-1" size={10} />
            </div>
          ) : null}
          {displayWidth + scrollX < svgWidth ? (
            <div
              className="flex items-center erda-gantt-calendar-arrow-right"
              onClick={() => onChangeScrollX(1)}
              style={{ left: offsetX + displayWidth - 16 }}
            >
              <ErdaIcon type="youfan" className="ml-1" size={10} />
            </div>
          ) : null}
        </div>
      );
      return bottomValues;
    };

    const getCalendarValuesForOther = () => {
      const topValues: ReactChild[] = [];
      const bottomValues: ReactChild[] = [];
      const ticks = viewMode === ViewMode.HalfDay ? 2 : 4;
      const topDefaultHeight = height * 0.5;
      const { dates } = dateSetup;
      for (let i = 0; i < dates.length; i++) {
        const date = dates[i];
        const bottomValue = getCachedDateTimeFormat(locale, {
          hour: 'numeric',
        }).format(date);

        bottomValues.push(
          <text
            key={date.getTime()}
            y={height * 0.8}
            x={columnWidth * (i + +rtl)}
            className={'erda-gantt-calendar-bottom-text'}
            fontFamily={fontFamily}
          >
            {bottomValue}
          </text>,
        );
        if (i === 0 || date.getDate() !== dates[i - 1].getDate()) {
          const topValue = `${date.getDate()} ${getLocaleMonth(date, locale)}`;
          topValues.push(
            <TopPartOfCalendar
              key={topValue + date.getFullYear()}
              value={topValue}
              x1Line={columnWidth * i + ticks * columnWidth}
              y1Line={0}
              y2Line={topDefaultHeight}
              xText={columnWidth * i + ticks * columnWidth * 0.5}
              yText={topDefaultHeight * 0.9}
            />,
          );
        }
      }

      return [topValues, bottomValues];
    };

    // let topValues: ReactChild[] = [];
    // let bottomValues: ReactChild[] = [];
    // switch (dateSetup.viewMode) {
    //   // case ViewMode.Month:
    //   //   [topValues, bottomValues] = getCalendarValuesForMonth();
    //   //   break;
    //   // case ViewMode.Week:
    //   //   [topValues, bottomValues] = getCalendarValuesForWeek();
    //   //   break;
    //   case ViewMode.Day:
    //     [topValues, bottomValues] = getCalendarValuesForDay();
    //     break;
    //   default:
    //     [topValues, bottomValues] = getCalendarValuesForOther();
    //     break;
    // }
    const finalWidth = max([columnWidth * dateSetup.dates.length, width]);

    return (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        className="overflow-visible"
        width={width}
        height={height}
        fontFamily={fontFamily}
      >
        <g className="erda-gantt-calendar" fontSize={fontSize}>
          <rect x={0} y={0} width={finalWidth} height={height} className={'erda-gantt-calendar-header'} />
          <foreignObject
            x={0}
            y={0}
            width={finalWidth}
            height={height}
            className={'erda-gantt-calendar-header overflow-visible'}
          >
            {getCalendarValuesForDay()}
          </foreignObject>
          {/* {topValues} */}
        </g>
      </svg>
    );
  },
)
Example #26
Source File: index.ts    From S2 with MIT License 4 votes vote down vote up
copyData = (
  sheetInstance: SpreadSheet,
  split: string,
  formatOptions?: FormatOptions,
): string => {
  const { isFormatHeader, isFormatData } = getFormatOptions(formatOptions);
  const { rowsHierarchy, rowLeafNodes, colLeafNodes, getCellMeta } =
    sheetInstance?.facet?.layoutResult;
  const { maxLevel } = rowsHierarchy;
  const { valueInCols } = sheetInstance.dataCfg.fields;
  // Generate the table header.
  const rowsHeader = rowsHierarchy.sampleNodesForAllLevels.map((item) =>
    sheetInstance.dataSet.getFieldName(item.key),
  );

  // get max query property length
  const rowLength = rowLeafNodes.reduce((pre, cur) => {
    const length = cur.query ? Object.keys(cur.query).length : 0;
    return length > pre ? length : pre;
  }, 0);

  // Generate the table body.
  let detailRows = [];
  let maxRowLength = 0;

  if (!sheetInstance.isPivotMode()) {
    detailRows = processValueInDetail(sheetInstance, split, isFormatData);
  } else {
    // Filter out the related row head leaf nodes.
    const caredRowLeafNodes = rowLeafNodes.filter((row) => row.height !== 0);

    for (const rowNode of caredRowLeafNodes) {
      let tempLine = [];
      if (isFormatHeader) {
        tempLine = getRowNodeFormatData(rowNode);
      } else {
        // Removing the space at the beginning of the line of the label.
        rowNode.label = trim(rowNode?.label);
        const id = rowNode.id.replace(ROOT_BEGINNING_REGEX, '');
        tempLine = id.split(ID_SEPARATOR);
      }
      // TODO 兼容下钻,需要获取下钻最大层级
      const totalLevel = maxLevel + 1;
      const emptyLength = totalLevel - tempLine.length;
      if (emptyLength > 0) {
        tempLine.push(...new Array(emptyLength));
      }

      // 指标挂行头且为平铺模式下,获取指标名称
      const lastLabel = sheetInstance.dataSet.getFieldName(last(tempLine));
      tempLine[tempLine.length - 1] = lastLabel;

      for (const colNode of colLeafNodes) {
        if (valueInCols) {
          const viewMeta = getCellMeta(rowNode.rowIndex, colNode.colIndex);
          tempLine.push(
            processValueInCol(viewMeta, sheetInstance, isFormatData),
          );
        } else {
          const viewMeta = getCellMeta(rowNode.rowIndex, colNode.colIndex);
          const lintItem = processValueInRow(
            viewMeta,
            sheetInstance,
            isFormatData,
          );
          if (isArray(lintItem)) {
            tempLine = tempLine.concat(...lintItem);
          } else {
            tempLine.push(lintItem);
          }
        }
      }
      maxRowLength = max([tempLine.length, maxRowLength]);
      const lineString = tempLine
        .map((value) => getCsvString(value))
        .join(split);

      detailRows.push(lineString);
    }
  }

  // Generate the table header.
  let headers: string[][] = [];

  if (isEmpty(colLeafNodes) && !sheetInstance.isPivotMode()) {
    // when there is no column in detail mode
    headers = [rowsHeader];
  } else {
    // 当列头label为array时用于补全其他层级的label
    let arrayLength = 0;
    // Get the table header of Columns.
    let tempColHeader = clone(colLeafNodes).map((colItem) => {
      let curColItem = colItem;

      const tempCol = [];

      // Generate the column dimensions.
      while (curColItem.level !== undefined) {
        let label = getHeaderLabel(curColItem.label);
        if (isArray(label)) {
          arrayLength = max([arrayLength, size(label)]);
        } else {
          // label 为数组时不进行格式化
          label = isFormatHeader ? getNodeFormatLabel(curColItem) : label;
        }
        tempCol.push(label);
        curColItem = curColItem.parent;
      }
      return tempCol;
    });

    if (arrayLength > 1) {
      tempColHeader = processColHeaders(tempColHeader, arrayLength);
    }

    const colLevels = tempColHeader.map((colHeader) => colHeader.length);
    const colLevel = max(colLevels);

    const colHeader: string[][] = [];
    // Convert the number of column dimension levels to the corresponding array.
    for (let i = colLevel - 1; i >= 0; i -= 1) {
      // The map of data set: key-name
      const colHeaderItem = tempColHeader
        // total col completion
        .map((item) =>
          item.length < colLevel
            ? [...new Array(colLevel - item.length), ...item]
            : item,
        )
        .map((item) => item[i])
        .map((colItem) => sheetInstance.dataSet.getFieldName(colItem));
      colHeader.push(flatten(colHeaderItem));
    }

    // Generate the table header.
    headers = colHeader.map((item, index) => {
      if (sheetInstance.isPivotMode()) {
        const { columns, rows, data } = sheetInstance.facet.cornerHeader.cfg;
        const colNodes = data.filter(
          ({ cornerType }) => cornerType === CornerNodeType.Col,
        );
        const rowNodes = data.filter(
          ({ cornerType }) => cornerType === CornerNodeType.Row,
        );

        if (index < colHeader.length - 1) {
          return [
            ...Array(rowLength - 1).fill(''),
            colNodes.find(({ field }) => field === columns[index])?.label || '',
            ...item,
          ];
        }
        if (index < colHeader.length) {
          return [
            ...rows.map(
              (row) => rowNodes.find(({ field }) => field === row)?.label || '',
            ),
            ...item,
          ];
        }

        return rowsHeader.concat(...item);
      }

      return index < colHeader.length
        ? Array(rowLength)
            .fill('')
            .concat(...item)
        : rowsHeader.concat(...item);
    });
  }

  const headerRow = headers
    .map((header) => {
      const emptyLength = maxRowLength - header.length;
      if (emptyLength > 0) {
        header.unshift(...new Array(emptyLength));
      }
      return header.map((h) => getCsvString(h)).join(split);
    })
    .join('\r\n');

  const data = [headerRow].concat(detailRows);
  const result = data.join('\r\n');
  return result;
}
Example #27
Source File: grid-body.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
GridBody: React.FC<GridBodyProps> = ({
  tasks: originTasks,
  dates,
  barTasks: tasks,
  rowHeight,
  svgWidth,
  columnWidth,
  todayColor,
  selectedTask,
  ganttHeight,
  setSelectedTask,
  rtl,
  onDateChange,
  ganttEvent,
  setRangeAddTime,
  horizontalRange,
  displayWidth,
  onMouseMove: propsOnMouseMove,
  mouseUnFocus: propsMouseUnFocus,
  mousePos,
}) => {
  let y = 0;
  const gridRows: ReactChild[] = [];
  const today = new Date();
  const dateDelta =
    dates[1].getTime() -
    dates[0].getTime() -
    dates[1].getTimezoneOffset() * 60 * 1000 +
    dates[0].getTimezoneOffset() * 60 * 1000;

  const [startPos, setStartPos] = React.useState<null | number[]>(null);
  const [endPos, setEndPos] = React.useState<null | number[]>(null);
  const [chosenTask, setChosenTask] = React.useState<Obj | null>(null);

  React.useEffect(() => {
    if (startPos && endPos) {
      setRangeAddTime({ x1: startPos[0], x2: endPos[0] });
    } else {
      setRangeAddTime(null);
    }
  }, [startPos, endPos]);

  const onMouseDown = (e: React.MouseEvent) => {
    const gridPos = e.currentTarget.getBoundingClientRect();
    const clickY = e.clientY - gridPos.y;
    const clickPos = Math.floor(clickY / rowHeight);
    const curTask = tasks[clickPos];

    if (!curTask.start || !curTask.end) {
      setSelectedTask(curTask.id);
      setChosenTask(curTask);

      setStartPos([
        Math.floor((e.clientX - gridPos.x) / columnWidth) * columnWidth,
        clickPos * rowHeight + 8,
        e.clientX - gridPos.x,
      ]);
    }
  };
  const mouseUnFocus = () => {
    propsMouseUnFocus();
    setStartPos(null);
    setEndPos(null);
    setChosenTask(null);
  };
  const curDates = dates.slice(...horizontalRange);
  const todayIndex = findIndex(curDates, (item) => moment(item).isSame(today, 'day'));
  const addTime = getDateFormX(startPos?.[0], endPos?.[0], dateDelta, columnWidth, curDates[0]?.getTime());

  const onMouseUp = () => {
    if (addTime.length && addTime[1] - addTime[0] >= dateDelta * 0.6 && chosenTask) {
      onDateChange({ ...chosenTask, start: new Date(addTime[0]), end: new Date(addTime[1]) });
    }
    mouseUnFocus();
  };
  const onMouseMove = (e: React.MouseEvent) => {
    const gridPos = e.currentTarget.getBoundingClientRect();
    propsOnMouseMove(e);
    const curEndPod = e.clientX - gridPos.x;

    if (startPos) {
      setEndPos(
        curEndPod - startPos[2] > 10
          ? [
              (Math.floor((e.clientX - gridPos.x + 1) / columnWidth) + 1) * columnWidth,
              startPos[1] + rowHeight - 16,
              curEndPod,
            ]
          : null,
      );
    }
  };

  tasks?.forEach((task: Task, idx: number) => {
    const validTask = task.start && task.end;
    let PointIcon = null;
    if (validTask) {
      const displayPos = Math.floor(displayWidth / columnWidth);
      if (curDates?.[0] && task.end < curDates[0]) {
        PointIcon = (
          <div className="text-default-2 hover:text-default-4 erda-gantt-grid-arrow-box flex items-center">
            <ErdaIcon className="cursor-pointer " type="zuo" size={20} onClick={() => setSelectedTask(task.id)} />
            <div className="erda-gantt-grid-arrow text-default-6">
              {moment(task.start).format('MM-DD')} ~ {moment(task.end).format('MM-DD')}
            </div>
          </div>
        );
      } else if (curDates?.[displayPos] && task.start > curDates[displayPos]) {
        PointIcon = (
          <div
            className="text-default-2 hover:text-default-4 erda-gantt-grid-arrow-box flex items-center"
            style={{ marginLeft: displayWidth - 20 - 80 }}
          >
            <div className="erda-gantt-grid-arrow text-default-6">
              {moment(task.start).format('MM-DD')} ~ {moment(task.end).format('MM-DD')}
            </div>
            <ErdaIcon className="cursor-pointer" onClick={() => setSelectedTask(task.id)} type="you" size={20} />
          </div>
        );
      }
    }
    gridRows.push(
      <foreignObject key={`Row${task.id}`} x="0" y={y} width={svgWidth} height={rowHeight}>
        <div
          className={`flex erda-gantt-grid-row h-full ${
            selectedTask?.id === task.id ? 'erda-gantt-grid-row-selected' : ''
          } ${!validTask ? 'on-add' : ''} ${mousePos?.[1] === idx ? 'on-hover' : ''}`}
        />
      </foreignObject>,
    );
    y += rowHeight;
  });

  const { changedTask } = ganttEvent || {};
  const realHeight = tasks.length * rowHeight;

  const getAddRangePos = () => {
    if (startPos && endPos) {
      return {
        transform: `translate(${min([startPos[0], endPos[0]])},${min([startPos[1], endPos[1]])})`,
        width: Math.abs(startPos[0] - endPos[0]),
        height: Math.abs(startPos[1] - endPos[1]),
      };
    }
    return null;
  };

  const getRangePos = () => {
    if (changedTask) {
      return {
        transform: `translate(${changedTask.x1},0)`,
        width: changedTask.x2 - changedTask.x1,
        height: max([ganttHeight, realHeight]),
      };
    } else if (startPos && endPos) {
      return {
        transform: `translate(${min([startPos[0], endPos[0]])},0)`,
        width: Math.abs(endPos[0] - startPos[0]),
        height: max([ganttHeight, realHeight]),
      };
    }
    return null;
  };

  const getMouseBlockPos = () => {
    if (mousePos) {
      const curTask = tasks[mousePos[1]];
      if (curTask && !curTask.start && !curTask.end) {
        return {
          width: columnWidth,
          height: rowHeight - 16,
          transform: `translate(${mousePos[0] * columnWidth},${mousePos[1] * rowHeight + 8})`,
        };
      }
    }
    return null;
  };

  const getMouseHoverPos = () => {
    if (mousePos) {
      return {
        width: 8,
        height: max([ganttHeight, realHeight]),
        transform: `translate(${mousePos[0] * columnWidth + columnWidth / 2 - 4},0)`,
      };
    }
    return null;
  };

  const rangePos = getRangePos();
  const mouseBlockPos = getMouseBlockPos();
  const addRangePos = getAddRangePos();
  const mouseHoverPos = getMouseHoverPos();
  const todayStartPos = todayIndex * columnWidth + columnWidth / 2 - 1;
  return (
    <g
      className="gridBody"
      onMouseDown={onMouseDown}
      onMouseUp={() => {
        onMouseUp();
      }}
      onMouseMove={onMouseMove}
      onMouseLeave={mouseUnFocus}
    >
      {rangePos ? (
        <rect {...rangePos} className="erda-gantt-grid-changed-range" />
      ) : mouseHoverPos ? (
        <foreignObject {...mouseHoverPos}>
          <div className="h-full w-full erda-gantt-grid-hover-box">
            <div className="erda-gantt-grid-hover-arrow" />
            <div className="erda-gantt-grid-hover-range h-full w-full" />
          </div>
        </foreignObject>
      ) : null}

      <g className="rows">{gridRows}</g>
      {addRangePos ? (
        <g>
          <foreignObject {...addRangePos}>
            <div className="erda-gantt-grid-add-rect text-sm text-desc  bg-white bg-opacity-100 w-full h-full">{`${moment(
              addTime[0],
            ).format('MM-DD')}~${moment(addTime[1]).format('MM-DD')}`}</div>
          </foreignObject>
        </g>
      ) : mouseBlockPos ? (
        <g>
          <foreignObject {...mouseBlockPos}>
            <div className="erda-gantt-grid-add-rect bg-white bg-opacity-100 w-full h-full" />
          </foreignObject>
        </g>
      ) : null}
      {todayIndex > -1 ? (
        <polyline
          points={`${todayStartPos + columnWidth / 2},4 ${todayStartPos + columnWidth / 2},${max([
            ganttHeight,
            realHeight,
          ])}`}
          className="erda-gantt-grid-today"
        />
      ) : null}
    </g>
  );
}
Example #28
Source File: tiled-filter-comp.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
TiledFilter = (props: IProps) => {
  const { fields, delay = 1000, value: propsValue, onChange, expand: propsExpand = true } = props;
  const [expand, setExpand] = React.useState(propsExpand);
  const [value, setValue] = React.useState(propsValue || {});
  const [labelWidth, setLabelWidth] = React.useState<string | number>('auto');

  useMount(() => {
    const labelEles = document.querySelectorAll('.tiled-fields-item-label');
    const maxWidth: number = max(map(labelEles, (ele: HTMLSpanElement) => ele.offsetWidth));
    setLabelWidth(Math.min(maxWidth, MAX_LABEL_WIDTH));
  });

  useUpdateEffect(() => {
    setValue(propsValue || {});
  }, [propsValue]);

  React.useEffect(() => {
    setExpand(propsExpand);
  }, [propsExpand]);

  const debouncedChange = React.useRef(debounce(onChange, delay));
  const debouncedInputChange = React.useRef(debounce(onChange, 1000));

  const inputFields: IField[] = [];
  const selectFields: IField[] = [];
  fields.forEach((field) => {
    if (field.type === 'input') {
      inputFields.push(field);
    } else if (field.type.includes('select')) {
      selectFields.push(field);
    }
  });

  const onChangeItem = (val: string, field: IField, forceUpdate?: boolean) => {
    if (forceUpdate) {
      setValue((prevV) => {
        const newVal = { ...prevV, [field.key]: val };
        debouncedChange.current(newVal, { [field.key]: val });
        return { ...prevV, [field.key]: val };
      });
      return;
    }
    const curValue = value[field.key];
    if (field.multiple) {
      setValue((prevV) => {
        const curChangeVal = curValue?.includes(val)
          ? curValue.filter((vItem: string) => vItem !== val)
          : (curValue || []).concat(val);
        const newVal = { ...prevV, [field.key]: curChangeVal };
        debouncedChange.current(newVal, { [field.key]: curChangeVal });
        return newVal;
      });
    } else {
      setValue((prevV) => {
        const curChangeVal = curValue === val ? undefined : val;
        const newVal = { ...prevV, [field.key]: curChangeVal };
        debouncedChange.current(newVal, { [field.key]: curChangeVal });
        return newVal;
      });
    }
  };

  const onChangeInputItem = (val: string, field: IField) => {
    setValue((prevV) => {
      const newVal = { ...prevV, [field.key]: val };
      debouncedInputChange.current(newVal, { [field.key]: val });
      return newVal;
    });
  };

  const clearSelect = () => {
    const newVal = {};
    selectFields.forEach((sItem) => {
      newVal[sItem.key] = undefined;
    });
    setValue((prev) => {
      debouncedChange.current({ ...prev, ...newVal }, { ...newVal });
      return { ...prev, ...newVal };
    });
  };

  const getValueLength = () => {
    let valLength = 0;
    selectFields.forEach((sItem) => {
      const curVal = value[sItem.key];
      if (Array.isArray(curVal)) {
        valLength += curVal.length;
      } else if (curVal !== undefined && curVal !== '') {
        valLength += 1;
      }
    });
    return valLength;
  };

  const curValLength = getValueLength();
  return (
    <div className="tiled-filter">
      <div className={`tiled-fields ${expand ? '' : 'no-expand'}`}>
        {selectFields.map((item) => {
          return (
            <SelectFieldItem
              key={item.key}
              field={item}
              value={value?.[item.key]}
              onChangeItem={onChangeItem}
              labelWidth={labelWidth}
            />
          );
        })}
      </div>
      <div className="flex justify-between items-center">
        <div className="flex items-center">
          {curValLength ? (
            <>
              <span>{`${i18n.t('{name} items selected', { name: curValLength })}`}</span>
              <span className="fake-link ml-2 mr-4" onClick={clearSelect}>
                {i18n.t('common:Clear selected')}
              </span>
            </>
          ) : null}
          <div className="tiled-input">
            {inputFields.map((inputItem) => {
              return (
                <Input
                  className={'tiled-input-item'}
                  key={inputItem.key}
                  value={value[inputItem.key]}
                  size="small"
                  allowClear
                  prefix={<ErdaIcon type="search1" size="16" />}
                  placeholder={inputItem.placeholder || i18n.t('press enter to search')}
                  onChange={(e) => onChangeInputItem(e.target.value, inputItem)}
                />
              );
            })}
          </div>
        </div>
        <div className={`flex items-center expand-area`} onClick={() => setExpand(!expand)}>
          <span className="mr-2">{expand ? i18n.t('fold') : i18n.t('expand')}</span>
          <ErdaIcon type="down" className={`expand-icon flex items-center ${expand ? 'expand' : ''}`} size="16" />
        </div>
      </div>
    </div>
  );
}
Example #29
Source File: corner-cell.ts    From S2 with MIT License 4 votes vote down vote up
protected drawCellText() {
    const { x } = this.getContentArea();
    const { y, height } = this.getCellArea();

    const textStyle = this.getTextStyle();
    const cornerText = this.getCornerText();

    // 当为树状结构下需要计算文本前收起展开的icon占的位置

    const maxWidth = this.getMaxTextWidth();
    const emptyPlaceholder = getEmptyPlaceholder(
      this.meta,
      this.spreadsheet.options.placeholder,
    );
    const text = getEllipsisText({
      text: cornerText,
      maxWidth,
      fontParam: textStyle,
      placeholder: emptyPlaceholder,
    });
    this.actualText = text;
    const ellipseIndex = text.indexOf('...');

    let firstLine = text;
    let secondLine = '';

    // 存在文字的省略号 & 展示为tree结构
    if (ellipseIndex !== -1 && this.spreadsheet.isHierarchyTreeType()) {
      // 剪裁到 ... 最有点的后1个像素位置
      const lastIndex = ellipseIndex + (isIPhoneX() ? 1 : 0);
      firstLine = cornerText.substr(0, lastIndex);
      secondLine = cornerText.slice(lastIndex);
      // 第二行重新计算...逻辑
      secondLine = getEllipsisText({
        text: secondLine,
        maxWidth,
        fontParam: textStyle,
      });
    }

    const { x: textX } = getTextPosition(
      {
        x: x + this.getTreeIconWidth(),
        y,
        width: maxWidth,
        height,
      },
      textStyle,
    );

    const textY = y + (isEmpty(secondLine) ? height / 2 : height / 4);
    // first line
    this.textShapes.push(
      renderText(
        this,
        [this.textShapes[0]],
        textX,
        textY,
        firstLine,
        textStyle,
      ),
    );

    // second line
    if (!isEmpty(secondLine)) {
      this.textShapes.push(
        renderText(
          this,
          [this.textShapes[1]],
          textX,
          y + height * 0.75,
          secondLine,
          textStyle,
        ),
      );
    }

    this.actualTextWidth = max([
      measureTextWidth(firstLine, textStyle),
      measureTextWidth(secondLine, textStyle),
    ]);
  }