Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new-log-viewer: Integrate latest clp-ffi-js with support for log-level filtering; Add log-level selector to StatusBar. #77

Merged
merged 51 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0e36cd9
Add log level selector component to StatusBar.
junhaoliao Sep 26, 2024
c25499f
Add "Log Level" label as a Chip to LogLevelSelect component.
junhaoliao Sep 26, 2024
c7666e1
Rename parameter in LogLevelChip's onSelectedLogLevelsChange callback.
junhaoliao Sep 26, 2024
578f42a
Replace inline style with a CSS class for the dummy MenuItem.
junhaoliao Sep 26, 2024
62b67d5
Add comparator to .sort() - Apply suggestions from code review
junhaoliao Sep 26, 2024
12ecbc6
Change select box behaviour to always enable the selected log level a…
junhaoliao Sep 30, 2024
e199006
Do not specify LogLevelChip tooltip color based on the log level; add…
junhaoliao Sep 30, 2024
d8b68d3
Limit LogLevelSelect listbox width.
junhaoliao Sep 30, 2024
6eb3ed8
Remove log-level deselection handling on the chips.
junhaoliao Sep 30, 2024
d1400f4
Rename log level `NONE` -> `UNKNOWN`; Add an "ALL" option to clear fi…
junhaoliao Sep 30, 2024
9149712
Add checkbox to LogLevelSelect to allow individual selections.
junhaoliao Sep 30, 2024
a55c600
Specify listbox placement to avoid the listbox moving as the select b…
junhaoliao Sep 30, 2024
f2a4096
Set max-width directly to 0 to prevent auto-resizing with the Select …
junhaoliao Sep 30, 2024
4d0f7c8
Fix an issue where clicking on some Option edge deselects all options.
junhaoliao Sep 30, 2024
a590f2b
Extract `PlacementLeftTooltip` and `PlacementRightTooltip` components…
junhaoliao Sep 30, 2024
e35d7cc
Refactor Tooltip components to use destructuring.
junhaoliao Sep 30, 2024
da65862
Extract components from LogLevelSelect.
junhaoliao Sep 30, 2024
e9e9afb
Rename `LOG_LEVEL.NONE` -> `LOG_LEVEL.UNKNOWN`.
junhaoliao Sep 30, 2024
682b01d
In the checkbox tooltip, place +/- icon on the left of the LEVEL name.
junhaoliao Sep 30, 2024
12ef8ad
Rename "ALL" option to "Clear filters"; move "Clear filters" to the t…
junhaoliao Sep 30, 2024
67dae77
Revert tsconfig changes now that we don't need to use `Array.toRevers…
junhaoliao Sep 30, 2024
22061bb
Move LogLevelSelect to the right of the LogEventNum button in the sta…
junhaoliao Sep 30, 2024
a60ba66
Remove unused React import from LogLevelChip.tsx.
junhaoliao Oct 1, 2024
a8972f5
Mitigate a rounding issue in JoyUI when calculating the listbox posit…
junhaoliao Oct 1, 2024
31c538f
Remove Roboto Mono font and adjust LogLevelChip styles.
junhaoliao Oct 1, 2024
288a2e8
Docs - Apply suggestions from code review
junhaoliao Oct 1, 2024
ce6b038
Docs & Reformat - Apply suggestions from code review
junhaoliao Oct 1, 2024
5309671
Document one-argument behaviour in `range`.
junhaoliao Oct 1, 2024
1f7071d
Inline PlacementXXXTooltip components.
junhaoliao Oct 1, 2024
7259067
Remove extraneous onMouseDown event handler.
junhaoliao Oct 1, 2024
da171dd
Change ClearFiltersOption value to null.
junhaoliao Oct 1, 2024
bfe6c0e
Remove colon from Log Event button label.
junhaoliao Oct 1, 2024
fae7441
Move the declaration of the `result` array closer to its usage within…
junhaoliao Oct 1, 2024
91ae520
Refactor `range` function parameter names `start` -> ``begin`, `stop`…
junhaoliao Oct 1, 2024
3b34d52
Refactor `range` function signature to better support varied argument…
junhaoliao Oct 1, 2024
d7e1352
Attach the select change handler to the <Option/>s instead; rename th…
junhaoliao Oct 1, 2024
a8e9147
Add check for undefined data-value attribute in LogLevelSelect.
junhaoliao Oct 1, 2024
b8b0682
Docs - Apply suggestions from code review
junhaoliao Oct 1, 2024
2d64c6c
Change log level select clear option value to INVALID_LOG_LEVEL_VALUE.
junhaoliao Oct 1, 2024
4ad92f4
Change "TRACE" & "DEBUG" chips' color to "success".
junhaoliao Oct 1, 2024
6d8e98a
Remove commented out code.
junhaoliao Oct 1, 2024
fe1c3b3
Merge branch 'main' into log-level-select
junhaoliao Oct 3, 2024
dffcce2
Change `LOG_LEVEL.NONE` to `LOG_LEVEL.UNKNOWN`.
junhaoliao Oct 3, 2024
03b40c0
Stop propagation from checkbox click to the option.
junhaoliao Oct 5, 2024
c634ea2
Merge remote-tracking branch 'junhao/log-level-select' into log-level…
junhaoliao Oct 5, 2024
b76ebef
Remove JsonlDecoder.ts as it should have been done when merging from …
junhaoliao Oct 5, 2024
22ab6da
Add / remove empty lines - Apply suggestions from code review
junhaoliao Oct 7, 2024
7a0989f
Merge branch 'main' into HEAD
davemarco Oct 11, 2024
aa064c1
tie button and add ir support
davemarco Oct 11, 2024
ec194cd
fix lint
davemarco Oct 11, 2024
4b412fd
fix seizure
davemarco Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions new-log-viewer/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" crossorigin href="https://fonts.gstatic.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap">
Copy link
Member Author

@junhaoliao junhaoliao Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently learned that we may want to limit the range of font weight imported to reduce the asset sizes. What do you think?

Suggested change
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono:[email protected]&display=swap">

Also shall we start using @import for font imports? e.g.

<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:[email protected]&display=swap');
</style>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Is there a webpack plugin that will automatically pick the used font weights?
  • Is @import better nowadays?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I can't find one. With that said, it might be better to use a CDN for font loading rather than serving the font files directly from the log-viewer, so that loading of the log viewer can be sped up if the font has been loaded locally by another site. (It can be another story when we release the Log Viewer with Desktop UI frameworks like Electron. )
  2. Online critics have said that @import defer loading of other resources, so we might not want to use @import given we have been managing all font loading in the same HTML file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Gotcha. I guess we can specify a weight then, when we need to add fonts.
  2. Gotcha.

</head>
<body>
<div id="root"></div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.log-level-chip {
/* stylelint-disable-next-line custom-property-pattern */
--Chip-radius: 0;

font-family: "Roboto Mono", monospace !important;
font-weight: 600 !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react";

import {
Chip,
Tooltip,
} from "@mui/joy";
import {DefaultColorPalette} from "@mui/joy/styles/types/colorSystem";

import {LOG_LEVEL} from "../../../typings/logs";

import "./LogLevelChip.css";


/**
* Maps log levels with colors from JoyUI's default color palette.
*/
const LOG_LEVEL_COLOR_MAP: Record<LOG_LEVEL, DefaultColorPalette> = Object.freeze({
[LOG_LEVEL.NONE]: "neutral",
[LOG_LEVEL.TRACE]: "neutral",
[LOG_LEVEL.DEBUG]: "neutral",
[LOG_LEVEL.INFO]: "primary",
[LOG_LEVEL.WARN]: "warning",
[LOG_LEVEL.ERROR]: "danger",
[LOG_LEVEL.FATAL]: "danger",
});

interface LogLevelChipProps {
name: string,
value: LOG_LEVEL,

onSelectedLogLevelsChange: (func: (value: LOG_LEVEL[]) => LOG_LEVEL[]) => void,
}
Copy link

@coderabbitai coderabbitai bot Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider simplifying the callback function type.

The props interface looks good overall, but the callback function type could be simplified for better readability.

Instead of:

onSelectedLogLevelsChange: (func: (value: LOG_LEVEL[]) => LOG_LEVEL[]) => void,

Consider using:

onSelectedLogLevelsChange: (updater: (currentLevels: LOG_LEVEL[]) => LOG_LEVEL[]) => void,

This change makes it clearer that the function is an updater function that takes the current levels and returns the new levels. It also improves readability without losing any type information.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sg. Renamed as setter.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junhaoliao, glad to hear that you've renamed the parameter. Thank you!

(_/)
( ^_^ )
(")(")


🧠 Learnings used
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/LogLevelChip.css:6-6
Timestamp: 2024-09-26T08:05:11.421Z
Learning: When overriding JoyUI's default styling, it's acceptable to use `!important` in CSS in this project.

If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!


/**
* Renders a log level chip.
*
* @param props
* @param props.name
* @param props.value
* @param props.onSelectedLogLevelsChange Callback to handle changes to selected log levels.
* @return
*/
const LogLevelChip = ({name, value, onSelectedLogLevelsChange}: LogLevelChipProps) => {
const handleChipClick = (ev: React.MouseEvent<HTMLButtonElement>) => {
ev.stopPropagation();
onSelectedLogLevelsChange((oldValue) => oldValue.filter((v) => v !== value));
};

return (
<Tooltip
color={LOG_LEVEL_COLOR_MAP[value]}
key={value}
title={name}
>
<Chip
className={"log-level-chip"}
color={LOG_LEVEL_COLOR_MAP[value]}
variant={"outlined"}
onClick={handleChipClick}
>
{name[0]}
</Chip>
</Tooltip>
);
};
export default LogLevelChip;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.log-level-select-render-value-box {
display: flex;
gap: 2px;
}

.log-level-select-render-value-box-label {
background-color: initial !important;
}
Copy link

@coderabbitai coderabbitai bot Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider avoiding the use of !important.

While the class name is specific and follows a consistent naming convention, the use of !important is generally discouraged as it can lead to specificity issues and make the CSS harder to maintain.

Consider increasing the specificity of the selector instead of using !important. For example:

-.log-level-select-render-value-box-label {
-    background-color: initial !important;
-}
+.log-level-select-render-value-box .log-level-select-render-value-box-label {
+    background-color: initial;
+}

This approach increases the specificity of the selector, potentially eliminating the need for !important. If this doesn't resolve the issue, consider reviewing the conflicting styles and adjusting the overall CSS structure.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.log-level-select-render-value-box-label {
background-color: initial !important;
}
.log-level-select-render-value-box .log-level-select-render-value-box-label {
background-color: initial;
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !important usage is to override the styles from JoyUI and therefore cannot be easily removed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junhaoliao, understood. If !important is necessary to override the styles from JoyUI, then it's appropriate in this case.

/(=´ x `=)\


✏️ Learnings added
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.css:6-8
Timestamp: 2024-09-26T08:04:40.476Z
Learning: In this codebase, using `!important` is acceptable when overriding styles from JoyUI components.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

109 changes: 109 additions & 0 deletions new-log-viewer/src/components/StatusBar/LogLevelSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, {
useCallback,
useState,
} from "react";

import {SelectValue} from "@mui/base/useSelect";
import {
Box,
Chip,
IconButton,
MenuItem,
Option,
Select,
SelectOption,
} from "@mui/joy";

import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";

import {
LOG_LEVEL,
LOG_LEVEL_NAMES,
MAX_LOG_LEVEL,
} from "../../../typings/logs";
import {range} from "../../../utils/data";
import LogLevelChip from "./LogLevelChip";

import "./index.css";


/**
* Renders a dropdown box for selecting log levels.
*
* @return
*/
const LogLevelSelect = () => {
const [selectedLogLevels, setSelectedLogLevels] = useState<LOG_LEVEL[]>([]);

const handleRenderValue = (selected: SelectValue<SelectOption<LOG_LEVEL>, true>) => (
<Box className={"log-level-select-render-value-box"}>
<Chip
className={"log-level-select-render-value-box-label"}
variant={"soft"}
>
Log Level
</Chip>
{selected.map((selectedOption) => (
<LogLevelChip
key={selectedOption.value}
name={selectedOption.label as string}
value={selectedOption.value}
onSelectedLogLevelsChange={setSelectedLogLevels}/>
))}
</Box>
);

const handleSelectChange = useCallback((
_: React.MouseEvent | React.KeyboardEvent | React.FocusEvent | null,
newValue: SelectValue<LOG_LEVEL, true>
) => {
if (0 === selectedLogLevels.length) {
const [singleSelectValue] = newValue;
setSelectedLogLevels(range(singleSelectValue as number, 1 + MAX_LOG_LEVEL));
} else {
setSelectedLogLevels(newValue.sort());
}
}, [selectedLogLevels]);

const handleSelectClearButtonClick = () => {
handleSelectChange(null, []);
};

const handleSelectClearButtonMouseDown = (ev: React.MouseEvent<HTMLButtonElement>) => {
ev.stopPropagation();
};

return (
<Select
multiple={true}
placeholder={"Log Level"}
renderValue={handleRenderValue}
size={"sm"}
value={selectedLogLevels}
variant={"soft"}
indicator={0 === selectedLogLevels.length ?
<KeyboardArrowUpIcon/> :
<IconButton
variant={"plain"}
onClick={handleSelectClearButtonClick}
onMouseDown={handleSelectClearButtonMouseDown}
>
<CloseRoundedIcon/>
</IconButton>}
onChange={handleSelectChange}
>
{/* Add a dummy MenuItem to avoid the first Option receiving focus. */}
<MenuItem sx={{display: "none"}}/>
Copy link

@coderabbitai coderabbitai bot Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace hidden MenuItem with proper focus management

Using a hidden MenuItem to prevent the first Option from receiving focus is a workaround that might not be the most effective solution. Consider utilizing the listboxProps property or adjusting the Select component's focus settings to manage the focus behaviour more elegantly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a JoyUI specific bug and the MenuItem was added as a mitigation.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junhaoliao, understood. Thank you for the clarification regarding the JoyUI bug and the use of the MenuItem as a mitigation.

(*·ω·)つ☕


✏️ Learnings added
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.tsx:0-0
Timestamp: 2024-09-26T08:09:26.714Z
Learning: In JoyUI, a hidden `MenuItem` may be added to prevent the first `Option` from receiving focus due to a specific bug. This is an acceptable mitigation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

{LOG_LEVEL_NAMES.map((logLevelName, index) => (
<Option
key={logLevelName}
value={index}
>
{logLevelName}
</Option>
))}
Copy link

@coderabbitai coderabbitai bot Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure Option values correspond to LOG_LEVEL constants

The Option components currently use the index as the value, assuming that the indices align with the LOG_LEVEL enum values. This could lead to issues if the order of LOG_LEVEL_NAMES changes. To prevent potential mismatches, consider mapping over the LOG_LEVEL constants directly.

Here's how you might adjust the code:

-{LOG_LEVEL_NAMES.map((logLevelName, index) => (
+{Object.values(LOG_LEVEL).map((logLevel) => (
     <Option
-        key={logLevelName}
-        value={index}
+        key={logLevel}
+        value={logLevel}
     >
-        {logLevelName}
+        {LOG_LEVEL_NAMES[logLevel]}
     </Option>
 ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Option
key={logLevelName}
value={index}
>
{logLevelName}
</Option>
))}
{Object.values(LOG_LEVEL).map((logLevel) => (
<Option
key={logLevel}
value={logLevel}
>
{LOG_LEVEL_NAMES[logLevel]}
</Option>
))}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.values(LOG_LEVEL) returns not only the values in enum LOG_LEVEL but also the key names, which means we will need to perform extra filtering here.

Also, LOG_LEVEL is a numerical enum with values starting from 0 and therefore index is safe to be used.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junhaoliao, thank you for the clarification. I understand now.

(・ω・)?


✏️ Learnings added
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.tsx:99-105
Timestamp: 2024-09-26T08:12:43.984Z
Learning: `LOG_LEVEL` is a numerical enum with values starting from 0, so using `index` in `LOG_LEVEL_NAMES.map((logLevelName, index) => ...)` is safe.

Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.tsx:99-105
Timestamp: 2024-09-26T08:12:43.984Z
Learning: Using `Object.values(LOG_LEVEL)` returns both the enum's numeric values and key names, so extra filtering is needed to obtain only the numeric values.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.tsx:0-0
Timestamp: 2024-09-26T08:09:26.853Z
Learning: In JoyUI, a hidden `MenuItem` may be added to prevent the first `Option` from receiving focus due to a specific bug. This is an acceptable mitigation.

</Select>
);
};
export default LogLevelSelect;
2 changes: 2 additions & 0 deletions new-log-viewer/src/components/StatusBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
copyPermalinkToClipboard,
UrlContext,
} from "../../contexts/UrlContextProvider";
import LogLevelSelect from "./LogLevelSelect";

import "./index.css";

Expand Down Expand Up @@ -37,6 +38,7 @@ const StatusBar = () => {
>
Status message
</Typography>
<LogLevelSelect/>
<Button
size={"sm"}
onClick={handleCopyLinkButtonClick}
Expand Down
19 changes: 19 additions & 0 deletions new-log-viewer/src/typings/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,28 @@ enum LOG_LEVEL {
FATAL
}

/**
* Key names in enum `LOG_LEVEL`.
*/
const LOG_LEVEL_NAMES = Object.freeze(
Object.values(LOG_LEVEL).filter((value) => "string" === typeof value)
);

/**
* Values in enum `LOG_LEVEL`.
*/
const LOG_LEVEL_VALUES = Object.freeze(
Object.values(LOG_LEVEL).filter((value) => "number" === typeof value)
);

const MAX_LOG_LEVEL = Math.max(...LOG_LEVEL_VALUES);


const INVALID_TIMESTAMP_VALUE = 0;

export {
INVALID_TIMESTAMP_VALUE,
LOG_LEVEL,
LOG_LEVEL_NAMES,
MAX_LOG_LEVEL,
};
38 changes: 38 additions & 0 deletions new-log-viewer/src/utils/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,45 @@ const getMapValueWithNearestLessThanOrEqualKey = <T>(
map.get(lowerBoundKey) as T;
};

/**
* Creates an array of numbers in a specified range [startNum, startNum + steps].
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This utility function was created with a reference to Python's build-in https://docs.python.org/3.12/library/stdtypes.html#range method.

*
* @param start The value of the start parameter (or `0` if the parameter was not supplied).
* @param stop The value of the stop parameter.
* @param step The value of the step parameter (or `1` if the parameter was not supplied).
* - For a positive step, the contents of a range r are determined by the formula `r[i] = start +
* step*i` where `i >= 0` and `r[i] < stop`.
* - For a negative step, the contents of the range are still determined by the formula `r[i] =
* start + step*i`, but the constraints are `i >= 0` and `r[i] > stop`.
* @return An array of numbers from `start` to `stop` (exclusive) with a step of `step`.
* @throws {Error} if `step` is 0.
*/
const range = (start: number, stop: Nullable<number> = null, step: number = 1): number[] => {
const result: number[] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this closer to its first use?


if (0 === step) {
throw new Error("Step cannot be zero.");
}
if (null === stop) {
stop = start;
start = 0;
}

if (0 < step) {
for (let i = start; i < stop; i += step) {
result.push(i);
}
} else {
for (let i = start; i > stop; i += step) {
result.push(i);
}
}

return result;
};

export {
getMapKeyByValue,
getMapValueWithNearestLessThanOrEqualKey,
range,
};
Loading