lodash#last JavaScript Examples

The following examples show how to use lodash#last. 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: ActivityFeed.js    From datapass with GNU Affero General Public License v3.0 5 votes vote down vote up
render() {
    const { showDetails } = this.state;

    const { events } = this.props;

    let eventsToDisplay = chain(events)
      .sortBy('updated_at')
      .reject(
        ({ name, diff }) => name === 'update' && isEmpty(getChangelog(diff))
      )
      .value();

    if (!showDetails && events.length > 0) {
      eventsToDisplay = [last(eventsToDisplay)];
    }

    return (
      <div>
        <div className="activity-head">
          <Button
            outline
            icon="eye"
            onClick={() => this.setState({ showDetails: !showDetails })}
          >
            {showDetails ? 'Cacher l’historique' : 'Voir l’historique'}
          </Button>
        </div>
        {eventsToDisplay.map(
          ({
            id,
            comment,
            name,
            updated_at,
            user: { email, given_name, family_name },
            diff,
          }) => (
            <EventItem
              key={id}
              comment={comment}
              name={name}
              updated_at={updated_at}
              email={email}
              family_name={family_name}
              given_name={given_name}
              diff={diff}
            />
          )
        )}
      </div>
    );
  }
Example #2
Source File: StatsCell.js    From covid19 with MIT License 5 votes vote down vote up
Success = ({ countries = [], country = 'usa' }) => {
  // Calculate stats
  const [counts, setCounts] = useState([])
  const stat = (key) =>
    commaNumber(last(map(orderBy(counts, 'date.date'), key)))
  useEffect(() => {
    setCounts(find(countries, ['iso', country])?.dailyCounts)
  }, [country])

  return (
    <div>
      <section>
        <StatChart data={counts} dataKey="newCases" color="green" />
        <Stat value={stat('newCases')} label="New cases" />
      </section>
      <section>
        <StatChart data={counts} dataKey="currentlyInfected" color="yellow" />
        <Stat value={stat('currentlyInfected')} label="Currently infected" />
      </section>
      <section>
        <StatChart data={counts} dataKey="totalCases" color="orange" />
        <Stat value={stat('totalCases')} label="Confirmed cases" />
      </section>
      <section>
        <StatChart data={counts} dataKey="totalDeaths" color="red" />
        <Stat value={stat('totalDeaths')} label="Confirmed deaths" />
      </section>
      <style jsx>{`
        div {
          display: grid;
          grid-gap: 1rem;
          margin-top: 2rem;
        }
        @media (min-width: 48em) {
          div {
            grid-template-columns: repeat(4, 1fr);
          }
        }
        section {
          position: relative;
          min-height: 8rem;
        }
        section :global(.recharts-responsive-container) {
          position: absolute !important;
          top: 0;
          left: 0;
          right: 0;
        }
      `}</style>
    </div>
  )
}
Example #3
Source File: enhancers.js    From plant-3d-explorer with GNU Affero General Public License v3.0 5 votes vote down vote up
forgeCameraPointsEnhancer = (scan) => {
  const poses = scan.camera.poses
  let index = 0

  return {
    ...scan,
    camera: {
      ...scan.camera,
      poses: orderBy(
        poses,
        (d) => d.photoUri
      ).map((point) => {
        const m3rotation = new THREE.Matrix3()
        m3rotation.set(
          ...point.rotmat[0],
          ...point.rotmat[1],
          ...point.rotmat[2]
        )
        m3rotation.transpose()
        m3rotation.multiplyScalar(-1)

        const v3position = new THREE.Vector3(
          point.tvec[0],
          point.tvec[1],
          point.tvec[2]
        ).applyMatrix3(m3rotation)

        function createM4Rot (rotmat) {
          const m4rotation = new THREE.Matrix4()
          m4rotation.set(
            ...point.rotmat[0], 0,
            ...point.rotmat[1], 0,
            ...point.rotmat[2], 0,
            0, 0, 0, 1
          )
          return m4rotation
        }

        const objM4rotation = createM4Rot()
        const objT = new THREE.Matrix4().makeRotationX(-Math.PI / 2)
        objM4rotation.transpose()
        objM4rotation.multiply(objT)

        const vueM4rotation = createM4Rot()
        const vueT = new THREE.Matrix4().makeRotationX(-Math.PI)
        vueM4rotation.transpose()
        vueM4rotation.multiply(vueT)

        return {
          index: index++,
          id: point.photoUri,
          fileName: last(point.photoUri.split('/')),
          ...point,
          v3position,
          objM4rotation,
          vueM4rotation
        }
      })
    }
  }
}
Example #4
Source File: [key].js    From hivemind with Apache License 2.0 4 votes vote down vote up
Page = () => {
  const { user } = useUser()
  const router = useRouter()
  const [timestamp, setTimestamp] = useState(
    typeof window === 'undefined'
      ? null
      : parseFloat(new URLSearchParams(location.search).get('timestamp'))
  )
  const { key } = router.query
  const { data, error } = useFetch(
    user ? user : null,
    `/api/mindmaps/${key}?timestamp=${timestamp || ''}`
  )
  const { data: edata, error: eerror } = useFetch(
    user ? user : null,
    `/api/timeline/events?key=${key}`
  )
  const [title, setTitle] = useState(key)

  useEffect(() => {
    if (user) {
      mutate(
        [`/api/mindmaps/${key}?timestamp=${timestamp || ''}`, user.token],
        null,
        true
      )
    }
  }, [user, timestamp, key])

  useEffect(() => {
    if (user) {
      mutate([`/api/timeline/events?key=${key}`, user.token], null, true)
    }
  }, [user, key])

  useEffect(() => {
    if (data && data.ok) {
      setTitle(data.data.meta.name)
    }
  }, [data])

  useEffect(() => {
    const handleRouteChange = (url) => {
      const fullURL = new URL(url, location.origin)
      const toTs = fullURL.searchParams.get('timestamp')
      const toTsF = parseFloat(toTs) || null

      if ((!toTsF && timestamp) || toTsF !== timestamp) {
        setTimestamp(toTsF)
      }
    }

    router.events.on('routeChangeComplete', handleRouteChange)

    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [router.events, timestamp])

  if (typeof user === 'undefined') {
    return <Spinner />
  }

  if (error && window.notify) {
    const options = {
      place: 'tr',
      message: 'Failed to fetch mind map!',
      type: 'danger',
      autoDismiss: 7,
    }

    window.notify(options)
  }

  if (eerror && window.notify) {
    const options = {
      place: 'tr',
      message: 'Failed to fetch events!',
      type: 'danger',
      autoDismiss: 7,
    }

    window.notify(options)
  }

  const gotEventData = !eerror && edata && edata.ok
  const cEvents = gotEventData && edata.data
  const prevDisabled = !gotEventData || timestamp === cEvents[0].lctime
  const nextDisabled = !gotEventData || timestamp === last(cEvents).lctime

  async function jump(to) {
    if (to === 'now') {
      await router.push('/mmaps/[key]', `/mmaps/${key}`, { shallow: true })
      setTimestamp(null)
    } else if (gotEventData) {
      let toTS, idx

      switch (to) {
        case 'first':
          toTS = cEvents[0].lctime
          break

        case 'prev':
          idx = timestamp
            ? findIndex(cEvents, { lctime: timestamp })
            : cEvents.length
          toTS = cEvents[idx - 1].lctime
          break

        case 'next':
          idx = timestamp
            ? findIndex(cEvents, { lctime: timestamp })
            : cEvents.length - 2
          toTS = cEvents[idx + 1].lctime
          break

        case 'last':
          toTS = last(cEvents).lctime
          break

        default:
          toTS = to
      }

      await router.push(
        '/mmaps/[key]',
        {
          pathname: `/mmaps/${key}`,
          query: { timestamp: toTS },
        },
        { shallow: true }
      )
      setTimestamp(toTS)
    }
  }

  if (user) {
    const output = [
      <Row key="title">
        <Col xs="auto" md={7}>
          <h3>
            {title}
            {timestamp ? (
              <>
                &nbsp;
                <small className={'text-muted'}>
                  {' '}
                  @ {new Date(timestamp * 1000).toLocaleString()}
                </small>
              </>
            ) : null}
          </h3>
        </Col>
        <Col xs="auto" md={5} className={'text-right'}>
          <ShowAll />
          <Fit />
          <Search />
          &nbsp;&nbsp;|&nbsp;
          <ToolTippedButton
            className="ml-1"
            outline
            color="secondary"
            id="tag"
            disabled={true}
            tooltip="Tag (Coming Soon)"
          >
            <Tag size={16} />
          </ToolTippedButton>
          <ToolTippedButton
            className="ml-1"
            outline
            color="secondary"
            id="first"
            disabled={prevDisabled}
            tooltip="First"
            onClick={() => jump('first')}
          >
            <SkipBack size={16} />
          </ToolTippedButton>
          <ToolTippedButton
            className="ml-1"
            outline
            color="secondary"
            id="prev"
            disabled={prevDisabled}
            tooltip="Previous"
            onClick={() => jump('prev')}
          >
            <Rewind size={16} />
          </ToolTippedButton>
          <ToolTippedButton
            className="ml-1"
            outline
            color="secondary"
            id="next"
            disabled={nextDisabled}
            tooltip="Next"
            onClick={() => jump('next')}
          >
            <FastForward size={16} />
          </ToolTippedButton>
          <ToolTippedButton
            className="ml-1"
            outline
            color="secondary"
            id="last"
            disabled={nextDisabled}
            tooltip="Last"
            onClick={() => jump('last')}
          >
            <SkipForward size={16} />
          </ToolTippedButton>
          &nbsp;&nbsp;|&nbsp;
          <Rename
            nameChangedCallBack={setTitle}
            disabled={!!timestamp}
            rootNode={get(data, ['data', 'meta'], {})}
          />
          <ToolTippedButton
            className="ml-1"
            outline
            color={timestamp ? 'secondary' : 'danger'}
            id="now"
            tooltip={timestamp ? 'Click to unlock' : 'Click to lock'}
            onClick={() => jump(timestamp ? 'now' : 'last')}
          >
            {timestamp ? <Lock size={16} /> : <Unlock size={16} />}
          </ToolTippedButton>
        </Col>
      </Row>,
    ]

    if (error && data) {
      output.push(
        <Row key="content">
          <Col>
            <Error statusCode={data.status} />
          </Col>
        </Row>
      )
    } else if (eerror && edata) {
      output.push(
        <Row key="content">
          <Col>
            <Error statusCode={edata.status} />
          </Col>
        </Row>
      )
    } else {
      output.push(
        <Row key="content">
          <Col>
            <MindMap
              data={data}
              edata={edata}
              timestamp={timestamp}
              jump={jump}
            />
          </Col>
        </Row>
      )
    }

    return output
  }

  return <AuthPrompt />
}
Example #5
Source File: Chart.js    From covid19 with MIT License 4 votes vote down vote up
Chart = ({
  dailyCounts = [],
  countries = [],
  enabledCountries,
  defaultCountry
  // log
}) => {
  // sort dailyCounts for all later operations
  const sortedDailyCounts = orderBy(dailyCounts, 'date.date')
  const offsets = calculateDayOffsets(sortedDailyCounts, defaultCountry)

  const offsetDailyCounts = sortedDailyCounts.map((origCount) => {
    // deep clone
    let count = JSON.parse(JSON.stringify(origCount))

    let offset = offsets[count.country.iso]
    if (!offset) {
      return count
    } // in case of benchmark

    let newDate = addDays(new Date(count.date.date), offset)

    // round date to the nearest day
    let offsetForRounded = new Date(newDate.getTime() + 12 * 60 * 60)
    let roundedDate = new Date(
      offsetForRounded.getFullYear(),
      offsetForRounded.getMonth(),
      offsetForRounded.getDate()
    )

    // extract the YYYY-DD-MM portion
    count.date.date = new Date(
      roundedDate.toISOString().substring(0, 10)
    ).toISOString()

    return count
  })
  const sortedOffsetDailyCounts = orderBy(offsetDailyCounts, 'date.date')

  // Assemble chart display
  let days = {}

  sortedOffsetDailyCounts.forEach((count) => {
    days[count.date.date] = days[count.date.date] || {}

    days[count.date.date][`${count.country.iso}TotalCases`] = count.totalCases
    days[count.date.date][`${count.country.iso}TotalDeaths`] = count.totalDeaths
  })

  const countryCounts = groupBy(sortedDailyCounts, 'country.iso')
  // Highest date in benchmark country
  const maxBenchmarkDate = last(countryCounts[defaultCountry]).date.date

  // Calculate X axis numbers to show how many days ahead / behind a country is
  for (const day in days) {
    const daysBehind = daysBetween(new Date(day), new Date(maxBenchmarkDate))
    days[day]['daysBehind'] = daysBehind
  }

  const behindOrAhead =
    (last(Object.values(days))?.daysBehind || -1) > 0 ? 'ahead of' : 'behind'
  let updated = new Date(last(countryCounts[defaultCountry]).date.createdAt)
  updated.setDate(updated.getDate() + 1) // JS dates suck
  updated = updated
    .toLocaleDateString()
    .replace('/2020', '')
    .replace('2020-', '')

  // Prepare chart data
  const [chartData, setChartData] = useState([])
  useEffect(() => {
    setChartData(
      Object.keys(days).map((day) => ({
        date: day,
        ...days[day]
      }))
    )
  }, [dailyCounts])
  // Sorting
  useEffect(() => {
    setChartData((chartData) => orderBy(chartData, 'date'))
  }, [dailyCounts])

  return (
    <ResponsiveContainer height={512} id="primary">
      <LineChart
        data={chartData}
        margin={{ top: 0, right: 10, bottom: 25, left: 15 }}
      >
        <XAxis
          dataKey="daysBehind"
          label={{
            value: `Days ${behindOrAhead} ${countryFromKey(
              defaultCountry,
              countries
            )} (last updated: ${updated})`,
            position: 'bottom'
          }}
        />
        <YAxis
          tickFormatter={yAxisFormatter}
          label={{
            value: 'Total COVID-19 Cases',
            angle: -90,
            position: 'left'
          }}
        />
        <Tooltip
          separator=": "
          formatter={(value, key) => [
            commaNumber(value),
            countryFromKey(key, countries)
          ]}
        />
        <CartesianGrid stroke={theme.colors.snow} strokeDasharray="8 8" />
        {enabledCountries.map((iso) => (
          <Line
            key={iso}
            type="monotone"
            dataKey={`${iso}TotalCases`}
            stroke={theme.colors[iso]}
            strokeWidth={defaultCountry === iso ? 6 : 3}
            dot={false}
            activeDot={{ r: 8 }}
            connectNulls
          />
        ))}
        <style>{`
          .recharts-label {
            fill: ${theme.colors.muted};
          }
          .recharts-default-tooltip {
            border-radius: 0.375rem;
          }
          .recharts-tooltip-label {
            color: ${theme.colors.muted};
            font-family: ${theme.fonts.sans};
            font-size: 2rem;
            line-height: 1.5;
          }
          #primary .recharts-tooltip-label:after {
            content: ' days';
          }
          #primary .recharts-tooltip-item {
            font-family: ${theme.fonts.serif};
            font-size: 1rem;
          }
          @media (prefers-color-scheme: dark) {
            .recharts-default-tooltip {
              background-color: #1e1e1e !important;
            }
            .recharts-label {
              fill: ${theme.colors.snow};
            }
            .recharts-tooltip-label {
              color: ${theme.colors.snow};
            }
            .recharts-layer:not(.recharts-active-dot) .recharts-dot {
              fill: #1e1e1e !important;
            }
            line.recharts-cartesian-axis-line,
            line.recharts-cartesian-axis-tick-line,
            .recharts-cartesian-grid line {
              opacity: 0.25 !important;
            }
          }
        `}</style>
      </LineChart>
    </ResponsiveContainer>
  )
}
Example #6
Source File: graph.js    From plant-3d-explorer with GNU Affero General Public License v3.0 4 votes vote down vote up
Chart = sceneWrapper(({ valueTransformFn, ifManualData, data, unit, containerWidth, containerHeight }) => {
  const [hoveredAngle, setHoveredAngle] = useHoveredAngle()
  const [selectedAngle, setSelectedAngle] = useSelectedAngle()
  const [colors] = useColor()
  const height = containerHeight - (37 * 1.6)

  const goal = data.goal

  const vertiaclTickNb = Math.max(
    data.fruitPoints.length,
    data.automated.length,
    (data.manual ? data.manual.length : 0)
  )

  const nb = vertiaclTickNb - 1 // intermediate variable used when creating the Array to plot angles and internodes sequences with the correct size

  const verticalTicks = Array(Math.ceil(vertiaclTickNb / 5))
    .fill()
    .map((_, i) => (i * 5) - (i !== 0 ? 1 : 0))
    .filter((d) => (vertiaclTickNb - d) > 3)
    // .concat(data.fruitPoints.length - 1)
    .concat(vertiaclTickNb - 1)
  verticalScale
    .domain([first(verticalTicks), last(verticalTicks)])
    .rangeRound([height, 5])

  const horizontalTicks = data.bounds

  horizontalScale
    .domain([first(horizontalTicks), last(horizontalTicks)])
    .rangeRound([0, containerWidth - 37])

  const barHeight = Math.floor(height / vertiaclTickNb)

  const points = data.automated
    .map((rad, i) => {
      return {
        datum: rad,
        x: horizontalScale(valueTransformFn(rad)) + pointsOffset,
        y: verticalScale(i)
      }
    })

  const manualPoints = (!ifManualData ? [] : data.manual)
    .map((rad, i) => {
      return {
        x: horizontalScale(valueTransformFn(rad)) + pointsOffset,
        y: verticalScale(i)
      }
    })

  const highligthed = [hoveredAngle, selectedAngle]
    .filter((d) => isNotNullAndUndefiend(d))
    .filter((d) => d < vertiaclTickNb)[0]

  return <Content>
    <TopPart>
      <HorizontalAxis>
        {
          horizontalTicks.map((index) => {
            return <HorizontalTick
              key={`horizontal-tick-${index}`}
              left={horizontalScale(index) + 34}
            >
              <span>
                {index} {unit}
              </span>
            </HorizontalTick>
          })
        }
        {
          isNotNullAndUndefiend(goal) && <GoalHorizontalTick
            key={'horizontal-tick-goal'}
            left={horizontalScale(goal) + 34}
          >
            <span>
              {goal} {unit}
            </span>

          </GoalHorizontalTick>
        }
      </HorizontalAxis>
      <GraphPart>
        <VerticalAxis>
          {
            verticalTicks.map((index) => {
              return <VerticalTick
                key={`vertical-tick-${index}`}
                top={verticalScale(index)}
              >
                {index + 1}
              </VerticalTick>
            })
          }
        </VerticalAxis>
        <SVG
          width={containerWidth - 37 + 5}
          height={height}
        >
          <Area d={area(points)} />
          <g>
            <Line d={line(points)} />
            {
              points.map((d, index) => {
                return <Point
                  key={`point-${index}`}
                  cx={d.x}
                  cy={d.y}
                />
              })
            }
          </g>
          {
            ifManualData && <g>
              <Line d={line(manualPoints)} red />
              {
                manualPoints.map((d, index) => {
                  return <Point
                    red
                    key={`manual-point-${index}`}
                    cx={d.x}
                    cy={d.y}
                  />
                })
              }
            </g>
          }
          {
            isNotNullAndUndefiend(goal) && <GoalLine
              x1={horizontalScale(goal)}
              x2={horizontalScale(goal)}
              y1={0}
              y2={height}
            />
          }
        </SVG>
      </GraphPart>
    </TopPart>
    <InteractorContainer
      width={containerWidth - 37}
      height={height}
      onMouseLeave={() => setHoveredAngle(null)}
    >
      {
        new Array(nb)
          .fill()
          .map((_, i) => {
            const d = points[i] || manualPoints[i]
            return <Interactor
              key={`interactor-${i}`}
              top={d.y - (barHeight * 0.5)}
              height={barHeight}
              selectedAngle={isNotNullAndUndefiend(selectedAngle)}
              selected={selectedAngle === i}
              hoveredColor={
                colors.organs[hoveredAngle]
                  ? colors.organs[hoveredAngle].rgb
                  : hoveredAngle % 2
                    ? colors.globalOrganColors[1].rgb
                    : colors.globalOrganColors[0].rgb
              }
              selectedColor={
                colors.organs[selectedAngle]
                  ? colors.organs[selectedAngle].rgb
                  : selectedAngle % 2
                    ? colors.globalOrganColors[1].rgb
                    : colors.globalOrganColors[0].rgb
              }
              hovered={hoveredAngle === i}
              onMouseEnter={() => setHoveredAngle(i)}
              onClick={() => {
                selectedAngle === i ? setSelectedAngle(null) : setSelectedAngle(i)
              }}
            />
          })
      }
      {
        isNotNullAndUndefiend(highligthed) && <div
          style={{
            position: 'absolute',
            width: 72,
            height: 70,
            top: -6 + verticalScale(highligthed) - 20,
            paddingTop: 10,
            left: -40,
            background: 'rgba(255, 255, 255, 0.8)'
          }}
        >
          <HighlightedIndex
            top={3}
            color={
              (isNotNullAndUndefiend(selectedAngle) &&
              !isNotNullAndUndefiend(hoveredAngle))
                ? colors.organs[selectedAngle + 1]
                  ? Color(colors.organs[selectedAngle + 1].rgb).toString()
                  : Color(colors.globalOrganColors[selectedAngle % 2 ? 0 : 1].rgb)
                    .toString()
                : isNotNullAndUndefiend(hoveredAngle)
                  ? colors.organs[hoveredAngle + 1]
                    ? Color(colors.organs[hoveredAngle + 1].rgb).toString()
                    : Color(colors.globalOrganColors[hoveredAngle % 2 ? 0 : 1]
                      .rgb)
                      .toString()
                  : darkGreen
            }
          >
            <HighlightedIndexContentTop>
              Org.<HighlightedIndexValue>
                {highligthed + 2}
              </HighlightedIndexValue>
              {/* {`Org.${highligthed + 2}`} */}
            </HighlightedIndexContentTop>
          </HighlightedIndex>

          <HighlightedIndex
            top={20}
            color={
              (isNotNullAndUndefiend(selectedAngle) && isNotNullAndUndefiend(hoveredAngle))
                ? Color('#84EEE6').toString()
                : Color('#78D89D').toString()
            }
          >
            <HighlightedIndexContent>
              <HighlightedIndexValue>
                {highligthed + 1}
              </HighlightedIndexValue>
              {/* {highligthed + 1} */}
            </HighlightedIndexContent>
          </HighlightedIndex>

          <HighlightedIndex
            top={20 + 20 - 7}
            color={
              (isNotNullAndUndefiend(selectedAngle) &&
              !isNotNullAndUndefiend(hoveredAngle))
                ? colors.organs[selectedAngle]
                  ? Color(colors.organs[selectedAngle].rgb).toString()
                  : Color(colors.globalOrganColors[selectedAngle % 2 ? 1 : 0]
                    .rgb)
                    .toString()
                : isNotNullAndUndefiend(hoveredAngle)
                  ? colors.organs[hoveredAngle]
                    ? Color(colors.organs[hoveredAngle].rgb).toString()
                    : Color(colors.globalOrganColors[hoveredAngle % 2 ? 1 : 0]
                      .rgb)
                      .toString()
                  : Color('#78D89D').toString()
            }
          >
            <HighlightedIndexContentBottom>
              Org.<HighlightedIndexValue>
                {highligthed + 1}
              </HighlightedIndexValue>
              {/* {`Org.${highligthed + 1}`} */}
            </HighlightedIndexContentBottom>
          </HighlightedIndex>
        </div>
      }
      {
        [selectedAngle, hoveredAngle]
          .filter((d) => isNotNullAndUndefiend(d))
          .map((d, i) => {
            return <div
              key={`interacted-angle-${i}`}
            >
              {
                (isNotNullAndUndefiend(data.automated[d])) && (
                  <HoveredPoint
                    key={'main'}
                    top={verticalScale(d + 1) + barHeight - 4}
                    left={horizontalScale(
                      valueTransformFn(data.automated[d])
                    ) + 37 - 7}
                  />
                )
              }
              {
                (ifManualData && isNotNullAndUndefiend(data.manual[d])) && (
                  <HoveredPoint
                    red
                    key={'secondary'}
                    top={verticalScale(d + 1) + barHeight - 4}
                    left={horizontalScale(
                      valueTransformFn(data.manual[d])
                    ) + 37 - 7}
                  />
                )
              }
            </div>
          })
      }
    </InteractorContainer>
  </Content>
})