diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 45682f184d7..024c62cec93 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1627,3 +1627,47 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return call(source, proctype)(arglist(arguments))
#define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3]))
+
+// Converts browser keycodes to BYOND keycodes.
+/proc/browser_keycode_to_byond(keycode)
+ keycode = text2num(keycode)
+ switch(keycode)
+ // letters and numbers
+ if(65 to 90, 48 to 57)
+ return ascii2text(keycode)
+ if(17)
+ return "Ctrl"
+ if(18)
+ return "Alt"
+ if(16)
+ return "Shift"
+ if(37)
+ return "West"
+ if(38)
+ return "North"
+ if(39)
+ return "East"
+ if(40)
+ return "South"
+ if(45)
+ return "Insert"
+ if(46)
+ return "Delete"
+ if(36)
+ return "Northwest"
+ if(35)
+ return "Southwest"
+ if(33)
+ return "Northeast"
+ if(34)
+ return "Southeast"
+ if(112 to 123)
+ return "F[keycode-111]"
+ if(96 to 105)
+ return "Numpad[keycode-96]"
+ if(188)
+ return ","
+ if(190)
+ return "."
+ if(189)
+ return "-"
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 1017603ff37..9f94d9ee7b0 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -121,7 +121,10 @@ Class Procs:
var/market_verb = "Customer"
var/payment_department = ACCOUNT_ENG
- var/ui_x //For storing and overriding ui dimensions
+ // For storing and overriding ui id and dimensions
+ var/tgui_id // ID of TGUI interface
+ var/ui_style // ID of custom TGUI style (optional)
+ var/ui_x // Default size of TGUI window, in pixels
var/ui_y
/obj/machinery/Initialize()
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm
index 2ec13890345..6ee539d7271 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/RPD.dm
@@ -174,7 +174,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
icon_state = "[icon_state]_preview"
/obj/item/pipe_dispenser
- name = "Rapid Piping Device (RPD)"
+ name = "Rapid Pipe Dispenser (RPD)"
desc = "A device used to rapidly pipe things."
icon = 'icons/obj/tools.dmi'
icon_state = "rpd"
@@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes)
assets.send(user)
- ui = new(user, src, ui_key, "rpd", name, 300, 550, master_ui, state)
+ ui = new(user, src, ui_key, "rpd", name, 425, 472, master_ui, state)
ui.open()
/obj/item/pipe_dispenser/ui_data(mob/user)
@@ -316,16 +316,14 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
playeffect = FALSE
if("mode")
var/n = text2num(params["mode"])
- if(n == 2 && !(mode&1) && !(mode&2))
- mode |= 3
- else if(mode&n)
+ if(mode & n)
mode &= ~n
else
mode |= n
-
if(playeffect)
spark_system.start()
playsound(get_turf(src), 'sound/effects/pop.ogg', 50, FALSE)
+ return TRUE
/obj/item/pipe_dispenser/pre_attack(atom/A, mob/user)
if(!user.IsAdvancedToolUser() || istype(A, /turf/open/space/transit))
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index d15a362bbc1..26a5a2f3717 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -6,6 +6,8 @@
anchored = FALSE
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+ ui_style = "nanotrasen"
ui_x = 500
ui_y = 600
@@ -13,7 +15,6 @@
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
- var/ui_style = "nanotrasen"
var/numeric_input = ""
var/timing = FALSE
diff --git a/code/modules/cargo/exports/tools.dm b/code/modules/cargo/exports/tools.dm
index 9e58e4ba954..7bee3017d9f 100644
--- a/code/modules/cargo/exports/tools.dm
+++ b/code/modules/cargo/exports/tools.dm
@@ -108,7 +108,7 @@
/datum/export/rpd
cost = 100
- unit_name = "rapid piping device"
+ unit_name = "rapid pipe dispenser"
export_types = list(/obj/item/pipe_dispenser)
/datum/export/singulo //failsafe in case someone decides to ship a live singularity to CentCom without the corresponding bounty
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 474a861a5c2..aafc431c489 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -81,12 +81,24 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
//Logs all hrefs, except chat pings
if(!(href_list["_src_"] == "chat" && href_list["proc"] == "ping" && LAZYLEN(href_list) == 2))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
-
+
//byond bug ID:2256651
if (asset_cache_job && asset_cache_job in completed_asset_jobs)
to_chat(src, "An error has been detected in how your client is receiving resources. Attempting to correct.... (If you keep seeing these messages you might want to close byond and reconnect)")
src << browse("...", "window=asset_cache_browser")
+ // Keypress passthrough
+ if(href_list["__keydown"])
+ var/keycode = browser_keycode_to_byond(href_list["__keydown"])
+ if(keycode)
+ keyDown(keycode)
+ return
+ if(href_list["__keyup"])
+ var/keycode = browser_keycode_to_byond(href_list["__keyup"])
+ if(keycode)
+ keyUp(keycode)
+ return
+
// Admin PM
if(href_list["priv_msg"])
cmd_admin_pm(href_list["priv_msg"],null)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index ccfddd3999e..d15dc610c33 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -318,7 +318,7 @@
data["PC_batteryicon"] = "batt_20.gif"
else
data["PC_batteryicon"] = "batt_5.gif"
- data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %"
+ data["PC_batterypercent"] = "[round(battery_module.battery.percent())]%"
data["PC_showbatteryicon"] = 1
else
data["PC_batteryicon"] = "batt_5.gif"
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index df3000d0fcc..18c6d7a5a0e 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -37,8 +37,8 @@
if (!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
assets.send(user)
-
- ui = new(user, src, ui_key, "ntos_main", "NTOS Main menu", 400, 500, master_ui, state)
+ ui = new(user, src, ui_key, "ntos_main", "NtOS Main menu", 400, 500, master_ui, state)
+ ui.set_style("ntos")
ui.open()
ui.set_autoupdate(state = 1)
@@ -68,10 +68,10 @@
switch(action)
if("PC_exit")
kill_program()
- return 1
+ return TRUE
if("PC_shutdown")
shutdown_computer()
- return 1
+ return TRUE
if("PC_minimize")
var/mob/user = usr
if(!active_program || !all_components[MC_CPU])
@@ -142,6 +142,7 @@
set_light(comp_light_luminosity, 1, comp_light_color)
else
set_light(0)
+ return TRUE
if("PC_light_color")
var/mob/user = usr
@@ -156,6 +157,7 @@
comp_light_color = new_color
light_color = new_color
update_light()
+ return TRUE
else
return
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index f94ac6cc065..95c635b771f 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -16,9 +16,9 @@
var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging.
var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable.
var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
- var/tgui_id // ID of TG UI interface
- var/ui_style // ID of custom TG UI style (optional)
- var/ui_x = 575 // Default size of TG UI window, in pixels
+ var/tgui_id // ID of TGUI interface
+ var/ui_style // ID of custom TGUI style (optional)
+ var/ui_x = 575 // Default size of TGUI window, in pixels
var/ui_y = 700
var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images!
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index 0439438cf0e..f7c734667b0 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -12,8 +12,9 @@
network_destination = "power monitoring system"
size = 9
tgui_id = "ntos_power_monitor"
- ui_x = 1200
- ui_y = 1000
+ ui_style = "ntos"
+ ui_x = 550
+ ui_y = 700
var/has_alert = 0
var/obj/structure/cable/attached_wire
diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
index bd2426a294d..1af0b6f96c2 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -9,8 +9,9 @@
network_destination = "supermatter monitoring system"
size = 5
tgui_id = "ntos_supermatter_monitor"
+ ui_style = "ntos"
ui_x = 600
- ui_y = 400
+ ui_y = 350
var/last_status = SUPERMATTER_INACTIVE
var/list/supermatters
var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal.
diff --git a/code/modules/power/monitor.dm b/code/modules/power/monitor.dm
index e0e34402952..5b1f8fb7276 100644
--- a/code/modules/power/monitor.dm
+++ b/code/modules/power/monitor.dm
@@ -10,8 +10,10 @@
idle_power_usage = 20
active_power_usage = 100
circuit = /obj/item/circuitboard/computer/powermonitor
- ui_x = 1200
- ui_y = 1000
+ tgui_id = "power_monitor"
+ ui_style = "ntos"
+ ui_x = 550
+ ui_y = 700
var/obj/structure/cable/attached_wire
var/obj/machinery/power/apc/local_apc
@@ -87,7 +89,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "power_monitor", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, tgui_id, name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/monitor/ui_data()
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index 6faf4aed69a..91e5ae08379 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -42,7 +42,6 @@
var/datum/tgui/master_ui
/// Children of this UI.
var/list/datum/tgui/children = list()
- var/titlebar = TRUE
var/custom_browser_id = FALSE
var/ui_screen = "home"
@@ -262,6 +261,9 @@
ui_screen = params["screen"]
SStgui.update_uis(src_object)
if("tgui:log")
+ // Force window to show frills on fatal errors
+ if(params["fatal"])
+ winset(user, window_id, "titlebar=1;can-resize=1;size=600x600")
log_message(params["log"])
if("tgui:link")
user << link(params["url"])
@@ -357,8 +359,5 @@
if(status == UI_DISABLED || push) // Update if the UI just because disabled, or a push is requested.
push_data(null, force = TRUE)
-/datum/tgui/proc/set_titlebar(value)
- titlebar = value
-
/datum/tgui/proc/log_message(message)
log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]")
diff --git a/tgui-next/README.md b/tgui-next/README.md
index 24733e36bff..3ee91752eb3 100644
--- a/tgui-next/README.md
+++ b/tgui-next/README.md
@@ -292,11 +292,23 @@ over the button.
- `bottom` (default) - Show tooltip below the button.
- `left` - Show tooltip on the left of the button.
- `right` - Show tooltip on the right of the button.
+- `ellipsis: boolean` - If button width is constrained, button text will
+be truncated with an ellipsis. Be careful however, because this prop breaks
+the baseline alignment.
- `title: string` - A native browser tooltip, which appears when hovering
over the button.
- `content/children: any` - Content to render inside the button.
- `onClick: function` - Called when element is clicked.
+### `Button.Checkbox`
+
+A ghetto checkbox, made entirely using existing Button API.
+
+Props:
+
+- See inherited props: [Button](#button)
+- `checked: boolean` - Boolean value, which marks the checkbox as checked.
+
### `ColorBox`
Displays a 1-character wide colored square. Can be used as a status indicator,
@@ -349,6 +361,10 @@ two flex items as far as possible from each other.
Props:
- See inherited props: [Box](#box)
+- `spacing: number` - Spacing between flex items, in integer units
+(1 unit - 0.5em). Does not directly relate to a flex css property
+(adds a modifier class under the hood), and only integer numbers are
+supported.
- `direction: string` - This establishes the main-axis, thus defining the
direction flex items are placed in the flex container.
- `row` (default) - left to right.
@@ -432,7 +448,7 @@ Props:
Props:
-- See inherited props: [Table.Cell](#table-cell)
+- See inherited props: [Table.Cell](#tablecell)
- `size: number` (default: 1) - Size of the column relative to other columns.
### `Icon`
diff --git a/tgui-next/packages/common/collections.js b/tgui-next/packages/common/collections.js
index a679aba217b..bbcea64cd88 100644
--- a/tgui-next/packages/common/collections.js
+++ b/tgui-next/packages/common/collections.js
@@ -35,7 +35,11 @@ export const map = iterateeFn => collection => {
return collection;
}
if (Array.isArray(collection)) {
- return collection.map(iterateeFn);
+ const result = [];
+ for (let i = 0; i < collection.length; i++) {
+ result.push(iterateeFn(collection[i], i, collection));
+ }
+ return result;
}
if (typeof collection === 'object') {
const hasOwnProperty = Object.prototype.hasOwnProperty;
@@ -97,20 +101,11 @@ export const sortBy = (...iterateeFns) => array => {
};
/**
- * A fast implementation of map.
+ * A version of map, but for mapping over two arrays instead of one.
+ * The iteratee is invoked with three arguments:
+ * (valueA, valueB, index).
*/
-export const fastMap = (array, iterateeFn) => {
- const result = [];
- for (let i = 0; i < array.length; i++) {
- result.push(iterateeFn(array[i], i));
- }
- return result;
-};
-
-/**
- * A version of fastMap, but for mapping over two arrays instead of one.
- */
-export const fastProduct = (arrayA, arrayB, iterateeFn) => {
+export const product = iterateeFn => (arrayA, arrayB) => {
const result = [];
for (let i = 0; i < arrayA.length; i++) {
result.push(iterateeFn(arrayA[i], arrayB[i], i));
@@ -121,7 +116,7 @@ export const fastProduct = (arrayA, arrayB, iterateeFn) => {
/**
* A fast implementation of reduce.
*/
-export const fastReduce = (array, reducerFn, initialValue) => {
+export const reduce = (reducerFn, initialValue) => array => {
const length = array.length;
let i;
let result;
@@ -138,3 +133,34 @@ export const fastReduce = (array, reducerFn, initialValue) => {
}
return result;
};
+
+/**
+ * Creates an array of grouped elements, the first of which contains
+ * the first elements of the given arrays, the second of which contains
+ * the second elements of the given arrays, and so on.
+ */
+export const zip = (...arrays) => {
+ if (arrays.length === 0) {
+ return;
+ }
+ const numArrays = arrays.length;
+ const numValues = arrays[0].length;
+ const result = [];
+ for (let valueIndex = 0; valueIndex < numValues; valueIndex++) {
+ const entry = [];
+ for (let arrayIndex = 0; arrayIndex < numArrays; arrayIndex++) {
+ entry.push(arrays[arrayIndex][valueIndex]);
+ }
+ result.push(entry);
+ }
+ return result;
+};
+
+/**
+ * This method is like "zip" except that it accepts iteratee to
+ * specify how grouped values should be combined. The iteratee is
+ * invoked with the elements of each group.
+ */
+export const zipWith = iterateeFn => (...arrays) => {
+ return map(values => iterateeFn(...values))(zip(...arrays));
+};
diff --git a/tgui-next/packages/common/fp.js b/tgui-next/packages/common/fp.js
index 8bed9781e20..4be45877e2c 100644
--- a/tgui-next/packages/common/fp.js
+++ b/tgui-next/packages/common/fp.js
@@ -39,6 +39,3 @@ export const compose = (...funcs) => {
return funcs.reduce((a, b) => (value, ...rest) =>
a(b(value, ...rest), ...rest));
};
-
-// TODO: Refactor all code which uses this export
-export { map } from './collections';
diff --git a/tgui-next/packages/common/vector.js b/tgui-next/packages/common/vector.js
index 7819d531619..967827f556e 100644
--- a/tgui-next/packages/common/vector.js
+++ b/tgui-next/packages/common/vector.js
@@ -1,4 +1,4 @@
-import { fastMap, fastProduct, fastReduce } from './collections';
+import { map, product, reduce } from './collections';
/**
* Creates a vector, with as many dimensions are there are arguments.
@@ -20,23 +20,23 @@ class Vector {
}
add(vec) {
- return new Vector(fastProduct(this.c, vec.c, ADD));
+ return new Vector(product(ADD)(this.c, vec.c));
}
subtract(vec) {
- return new Vector(fastProduct(this.c, vec.c, SUB));
+ return new Vector(product(SUB)(this.c, vec.c));
}
multiply(n) {
- return new Vector(fastMap(this.c, x => x * n));
+ return new Vector(map(x => x * n)(this.c));
}
divide(n) {
- return new Vector(fastMap(this.c, x => x / n));
+ return new Vector(map(x => x / n)(this.c));
}
magnitude() {
- return Math.sqrt(fastReduce(fastProduct(this.c, this.c, MUL), ADD));
+ return Math.sqrt(reduce(ADD)(product(MUL)(this.c, this.c)));
}
normalize() {
diff --git a/tgui-next/packages/tgui/public/images/tgui-bg-nanotrasen.svg b/tgui-next/packages/tgui/assets/bg-nanotrasen.svg
similarity index 100%
rename from tgui-next/packages/tgui/public/images/tgui-bg-nanotrasen.svg
rename to tgui-next/packages/tgui/assets/bg-nanotrasen.svg
diff --git a/tgui-next/packages/tgui/public/images/tgui-bg-syndicate.svg b/tgui-next/packages/tgui/assets/bg-syndicate.svg
similarity index 100%
rename from tgui-next/packages/tgui/public/images/tgui-bg-syndicate.svg
rename to tgui-next/packages/tgui/assets/bg-syndicate.svg
diff --git a/tgui-next/packages/tgui/components/Box.js b/tgui-next/packages/tgui/components/Box.js
index 41c418e5632..08b4ab72506 100644
--- a/tgui-next/packages/tgui/components/Box.js
+++ b/tgui-next/packages/tgui/components/Box.js
@@ -83,6 +83,16 @@ const styleMapperByPropName = {
color: mapColorPropTo('color'),
textColor: mapColorPropTo('color'),
backgroundColor: mapColorPropTo('background-color'),
+ // Utility props
+ fillPositionedParent: (style, value) => {
+ if (value) {
+ style['position'] = 'absolute';
+ style['top'] = 0;
+ style['bottom'] = 0;
+ style['left'] = 0;
+ style['right'] = 0;
+ }
+ },
};
export const computeBoxProps = props => {
@@ -124,6 +134,7 @@ export const Box = props => {
...rest
} = props;
const color = props.textColor || props.color;
+ const backgroundColor = props.backgroundColor;
// Render props
if (typeof children === 'function') {
return children(computeBoxProps(props));
@@ -136,6 +147,7 @@ export const Box = props => {
classes([
className,
isColorClass(color) && 'color-' + color,
+ isColorClass(backgroundColor) && 'color-bg-' + backgroundColor,
]),
content || children,
ChildFlags.UnknownChildren,
diff --git a/tgui-next/packages/tgui/components/Button.js b/tgui-next/packages/tgui/components/Button.js
index dfd8983b091..36aa3ca69b1 100644
--- a/tgui-next/packages/tgui/components/Button.js
+++ b/tgui-next/packages/tgui/components/Button.js
@@ -19,6 +19,7 @@ export const Button = props => {
selected,
tooltip,
tooltipPosition,
+ ellipsis,
content,
children,
onclick,
@@ -43,9 +44,10 @@ export const Button = props => {
disabled && 'Button--disabled',
selected && 'Button--selected',
hasContent && 'Button--hasContent',
+ ellipsis && 'Button--ellipsis',
(color && typeof color === 'string')
? 'Button--color--' + color
- : 'Button--color--normal',
+ : 'Button--color--default',
className,
])}
tabIndex={!disabled && '0'}
@@ -89,3 +91,16 @@ export const Button = props => {
};
Button.defaultHooks = pureComponentHooks;
+
+export const ButtonCheckbox = props => {
+ const { checked, ...rest } = props;
+ return (
+
+ );
+};
+
+Button.Checkbox = ButtonCheckbox;
diff --git a/tgui-next/packages/tgui/components/Chart.js b/tgui-next/packages/tgui/components/Chart.js
new file mode 100644
index 00000000000..d7d51cb697b
--- /dev/null
+++ b/tgui-next/packages/tgui/components/Chart.js
@@ -0,0 +1,121 @@
+import { map, zipWith } from 'common/collections';
+import { Component, createRef } from 'inferno';
+import { Box } from './Box';
+import { pureComponentHooks } from 'common/react';
+import { tridentVersion } from '../byond';
+
+const normalizeData = (data, scale, rangeX, rangeY) => {
+ if (data.length === 0) {
+ return [];
+ }
+ const min = zipWith(Math.min)(...data);
+ const max = zipWith(Math.max)(...data);
+ if (rangeX !== undefined) {
+ min[0] = rangeX[0];
+ max[0] = rangeX[1];
+ }
+ if (rangeY !== undefined) {
+ min[1] = rangeY[0];
+ max[1] = rangeY[1];
+ }
+ const normalized = map(point => {
+ return zipWith((value, min, max, scale) => {
+ return (value - min) / (max - min) * scale;
+ })(point, min, max, scale);
+ })(data);
+ return normalized;
+};
+
+const dataToPolylinePoints = data => {
+ let points = '';
+ for (let i = 0; i < data.length; i++) {
+ const point = data[i];
+ points += point[0] + ',' + point[1] + ' ';
+ }
+ return points;
+};
+
+class LineChart extends Component {
+ constructor(props) {
+ super(props);
+ this.ref = createRef();
+ this.state = {
+ // Initial guess for viewBox dimentions
+ viewBox: [600, 200],
+ };
+ this.handleResize = () => {
+ const element = this.ref.current;
+ this.setState({
+ viewBox: [element.offsetWidth, element.offsetHeight],
+ });
+ };
+ }
+
+ componentDidMount() {
+ window.addEventListener('resize', this.handleResize);
+ this.handleResize();
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.handleResize);
+ }
+
+ render() {
+ const {
+ data = [],
+ rangeX,
+ rangeY,
+ fillColor = 'none',
+ strokeColor = '#ffffff',
+ strokeWidth = 2,
+ ...rest
+ } = this.props;
+ const { viewBox } = this.state;
+ const normalized = normalizeData(data, viewBox, rangeX, rangeY);
+ // Push data outside viewBox and form a fillable polygon
+ if (normalized.length > 0) {
+ const first = normalized[0];
+ const last = normalized[normalized.length - 1];
+ normalized.push([viewBox[0] + strokeWidth, last[1]]);
+ normalized.push([viewBox[0] + strokeWidth, -strokeWidth]);
+ normalized.push([-strokeWidth, -strokeWidth]);
+ normalized.push([-strokeWidth, first[1]]);
+ }
+ const points = dataToPolylinePoints(normalized);
+ return (
+
| + {area.name} + | +
+ |
+ + {area.load} + | +
+ |
+
+ |
+
+ |
+