d3-parliament-chart
Advanced tools
Comparing version 0.0.5 to 0.0.6
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = {})); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {})); | ||
}(this, (function (exports) { 'use strict'; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
/** | ||
* Generate the points and angle for a single section of the parliament | ||
* | ||
* @param seats - number of seats in this section | ||
* @param startRad - start radians of this section | ||
* @param endRad - end radians of this section | ||
* @param seatRadius - radius of the seats | ||
* @param rowHeight - height of the row | ||
* @param graphicHeight - height of the graphic | ||
* @param sectionGap - The gap between sections | ||
* @returns {[]} | ||
*/ | ||
const generatePartial = ({ | ||
seats, startRad, endRad, seatRadius, rowHeight, graphicHeight, sectionGap, | ||
}) => { | ||
// Calculate the radius of the graph, because we don't want the poitns to extend | ||
// beyond the width of the graphic | ||
const graphRadius = graphicHeight - seatRadius; | ||
return obj; | ||
} | ||
// Final Array | ||
let points = []; | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
// Which row we are currently drawing | ||
let currentRow = 0; | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
if (enumerableOnly) symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
}); | ||
keys.push.apply(keys, symbols); | ||
} | ||
// Create all the points | ||
while (points.length < seats) { | ||
// The radius of the row we are currently drawing | ||
const currentRowRadius = graphRadius - (rowHeight * currentRow); | ||
return keys; | ||
} | ||
// We need to also justify for the gap of the section, which radians value varies per row | ||
const currentRowGapRad = Math.atan((seatRadius + (sectionGap / 2)) / currentRowRadius); | ||
const currentRowStartRad = startRad + currentRowGapRad; | ||
const currentRowEndRad = endRad - currentRowGapRad; | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
// If our data doesn't fit inside the row or the graph, we just stop | ||
if (currentRowEndRad <= currentRowStartRad || currentRowRadius <= 0) break; | ||
if (i % 2) { | ||
ownKeys(Object(source), true).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
} else if (Object.getOwnPropertyDescriptors) { | ||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); | ||
} else { | ||
ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
} | ||
// Find the minimum size step given the radius | ||
const currRadStep = Math.atan((2.5 * (seatRadius)) / currentRowRadius); | ||
return target; | ||
} | ||
// Find how many seats are in this row | ||
const rowSeats = Math.min( | ||
Math.floor((currentRowEndRad - currentRowStartRad) / currRadStep), | ||
seats - points.length - 1, | ||
); | ||
function _objectWithoutPropertiesLoose(source, excluded) { | ||
if (source == null) return {}; | ||
var target = {}; | ||
var sourceKeys = Object.keys(source); | ||
var key, i; | ||
// Get adjusted step size so that things are justified evenly | ||
// edge case if there is only one seat in this row | ||
const roundedRadStep = rowSeats | ||
? (currentRowEndRad - currentRowStartRad) / rowSeats | ||
: 0; | ||
for (i = 0; i < sourceKeys.length; i++) { | ||
key = sourceKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
target[key] = source[key]; | ||
} | ||
// Add all the seats in this row | ||
for (let currSeat = 0; currSeat <= rowSeats; currSeat += 1) { | ||
// Get the current angle of the seat we are drawing | ||
const currentAngle = rowSeats | ||
? (currSeat * roundedRadStep + currentRowStartRad) | ||
// edge case if there is only one seat in this row, we put it in the middle | ||
: ((currentRowStartRad + currentRowEndRad) / 2); | ||
return target; | ||
} | ||
function _objectWithoutProperties(source, excluded) { | ||
if (source == null) return {}; | ||
var target = _objectWithoutPropertiesLoose(source, excluded); | ||
var key, i; | ||
if (Object.getOwnPropertySymbols) { | ||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source); | ||
for (i = 0; i < sourceSymbolKeys.length; i++) { | ||
key = sourceSymbolKeys[i]; | ||
if (excluded.indexOf(key) >= 0) continue; | ||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; | ||
target[key] = source[key]; | ||
} | ||
// convert the angle to x y coordinates | ||
points = points.concat([{ | ||
x: Math.cos(currentAngle) | ||
* (graphRadius - (rowHeight * currentRow)) | ||
+ graphicHeight, | ||
// flip the y coordinates | ||
y: graphicHeight - (Math.sin(currentAngle) | ||
* (graphRadius - (rowHeight * currentRow)) | ||
+ seatRadius) | ||
// Add seatRadius and any sectionGap / 4 so that we vertically center | ||
+ seatRadius + (sectionGap / 4), | ||
angle: currentAngle, | ||
}]); | ||
} | ||
return target; | ||
currentRow += 1; | ||
} | ||
return points; | ||
}; | ||
function _toConsumableArray(arr) { | ||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); | ||
} | ||
/** | ||
* Generates the list of all points and their x and y positions | ||
* | ||
* @param totalPoints - total number of points we want to draw | ||
* @param sections - total number of sections we want to draw | ||
* @param sectionGap - gap between sections | ||
* @param seatRadius - radius of each seat | ||
* @param rowHeight - height of each row | ||
* @param graphicWidth - width of the entire graphic | ||
* @returns {this} | ||
*/ | ||
var getParliamentPoints = (totalPoints, { sections, sectionGap, seatRadius, rowHeight }, graphicWidth) => { | ||
// Calculate the graphic height | ||
const graphicHeight = graphicWidth / 2; | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) return _arrayLikeToArray(arr); | ||
} | ||
// Get the number of final sections | ||
const finalSections = Math.min(totalPoints, sections); | ||
function _iterableToArray(iter) { | ||
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); | ||
} | ||
// Angle step per section in radians | ||
const radStep = Math.PI / finalSections; | ||
function _unsupportedIterableToArray(o, minLen) { | ||
if (!o) return; | ||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); | ||
var n = Object.prototype.toString.call(o).slice(8, -1); | ||
if (n === "Object" && o.constructor) n = o.constructor.name; | ||
if (n === "Map" || n === "Set") return Array.from(o); | ||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); | ||
} | ||
// Divide the seats evenly among the sections, while also calculating | ||
// the start radians and end radians. | ||
const sectionObjs = Array(finalSections) | ||
// First evenly divide the seats into each section | ||
.fill({ seats: Math.floor(totalPoints / finalSections) }) | ||
// add the start and end radians | ||
.map((a, i) => ({ ...a, startRad: i * radStep, endRad: (i + 1) * radStep })); | ||
function _arrayLikeToArray(arr, len) { | ||
if (len == null || len > arr.length) len = arr.length; | ||
// There are leftover seats that we need to fit into sections | ||
// Calculate how many there are | ||
let leftoverSeats = totalPoints % finalSections; | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
// If leftover seats is 0, we can skip this entire section | ||
if (leftoverSeats !== 0) { | ||
// We want to add the leftover seats to the center section first, then move outward | ||
// We do this by separating the sections into two arrays, left and right | ||
const right = Array(finalSections).fill(null).map((c, i) => i); | ||
const left = right.splice(0, Math.floor(finalSections / 2)).reverse(); | ||
return arr2; | ||
} | ||
// Add the seats | ||
while (leftoverSeats > 0) { | ||
// Whichever array is longer, we pop from that array and add to that section first | ||
if (left.length >= right.length) sectionObjs[left.shift()].seats += 1; | ||
else sectionObjs[right.shift()].seats += 1; | ||
function _nonIterableSpread() { | ||
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
// decrement leftoverSeats by one | ||
leftoverSeats -= 1; | ||
} | ||
} | ||
/** | ||
* Generate the points and angle for a single section of the parliament | ||
* | ||
* @param seats - number of seats in this section | ||
* @param startRad - start radians of this section | ||
* @param endRad - end radians of this section | ||
* @param seatRadius - radius of the seats | ||
* @param rowHeight - height of the row | ||
* @param graphicHeight - height of the graphic | ||
* @param sectionGap - The gap between sections | ||
* @returns {[]} | ||
*/ | ||
var generatePartial = function generatePartial(_ref) { | ||
var seats = _ref.seats, | ||
startRad = _ref.startRad, | ||
endRad = _ref.endRad, | ||
seatRadius = _ref.seatRadius, | ||
rowHeight = _ref.rowHeight, | ||
graphicHeight = _ref.graphicHeight, | ||
sectionGap = _ref.sectionGap; | ||
// Calculate the radius of the graph, because we don't want the poitns to extend | ||
// beyond the width of the graphic | ||
var graphRadius = graphicHeight - seatRadius; // Final Array | ||
// Call the section partial generation tool for each section | ||
return sectionObjs.map((s) => generatePartial({ | ||
...s, seatRadius, rowHeight, graphicHeight, sectionGap, | ||
})) | ||
// flatten the array | ||
.reduce((acc, val) => [...acc, ...val], []) | ||
// sort by angle | ||
.sort((a, b) => b.angle - a.angle) | ||
// remove angle from returned dataset | ||
.map((r) => { | ||
const { angle, ...rest } = r; | ||
return rest; | ||
}); | ||
}; | ||
var points = []; // Which row we are currently drawing | ||
const debugColor = '#1abc9c'; | ||
var currentRow = 0; // Create all the points | ||
/** | ||
* Draw Debugging circle and section guides. | ||
*/ | ||
var debugGuides = (selection, graphicWidth, options, totalSeats) => { | ||
const sections = Math.min(totalSeats, options.sections); | ||
while (points.length < seats) { | ||
// The radius of the row we are currently drawing | ||
var currentRowRadius = graphRadius - rowHeight * currentRow; // We need to also justify for the gap of the section, which radians value varies per row | ||
// Create a container for our debug lines | ||
const g = selection.append('g') | ||
.attr('class', 'debug') | ||
.attr('transform', `translate(0, ${options.sectionGap / 4})`); | ||
var currentRowGapRad = Math.atan((seatRadius + sectionGap / 2) / currentRowRadius); | ||
var currentRowStartRad = startRad + currentRowGapRad; | ||
var currentRowEndRad = endRad - currentRowGapRad; // If our data doesn't fit inside the row or the graph, we just stop | ||
// The radius of the semicircle | ||
const radius = graphicWidth / 2; | ||
if (currentRowEndRad <= currentRowStartRad || currentRowRadius <= 0) break; // Find the minimum size step given the radius | ||
// Semicircle frame | ||
g.append('path') | ||
.attr('d', `M${graphicWidth},${radius} a1,1 0 0,0 -${graphicWidth},0 m 0 -${options.sectionGap / 2} l ${graphicWidth} 0`) | ||
.attr('stroke', debugColor) | ||
.attr('stroke-width', 2) | ||
.attr('stroke-dasharray', '5 5') | ||
.attr('fill', 'none'); | ||
var currRadStep = Math.atan(2.5 * seatRadius / currentRowRadius); // Find how many seats are in this row | ||
// Section borders | ||
const radStep = Math.PI / sections; | ||
for (let i = 1; i < sections; i += 1) { | ||
const radAngle = radStep * i; | ||
var rowSeats = Math.min(Math.floor((currentRowEndRad - currentRowStartRad) / currRadStep), seats - points.length - 1); // Get adjusted step size so that things are justified evenly | ||
// edge case if there is only one seat in this row | ||
// If the section gap is 0 we only need to draw one line | ||
if (options.sectionGap <= 0) { | ||
g.append('line') | ||
.attr('x1', radius).attr('y1', radius) | ||
.attr('x2', (Math.cos(radAngle) * radius) + radius) | ||
.attr('y2', radius - (Math.sin(radAngle) * radius)) | ||
.attr('stroke', debugColor) | ||
.attr('stroke-width', 2) | ||
.attr('stroke-dasharray', '5 5'); | ||
} else { | ||
// Calculate the offset and draw two lines. | ||
const offsetX = Math.sin(radAngle) * (options.sectionGap / 2); | ||
const offsetY = Math.cos(radAngle) * (options.sectionGap / 2); | ||
var roundedRadStep = rowSeats ? (currentRowEndRad - currentRowStartRad) / rowSeats : 0; // Add all the seats in this row | ||
g.append('line') | ||
.attr('x1', radius - offsetX) | ||
.attr('y1', radius - offsetY) | ||
.attr('x2', ((Math.cos(radAngle) * radius) + radius) - offsetX) | ||
.attr('y2', (radius - (Math.sin(radAngle) * radius)) - offsetY) | ||
.attr('stroke', debugColor) | ||
.attr('stroke-width', 2) | ||
.attr('stroke-dasharray', '5 5'); | ||
for (var currSeat = 0; currSeat <= rowSeats; currSeat += 1) { | ||
// Get the current angle of the seat we are drawing | ||
var currentAngle = rowSeats ? currSeat * roundedRadStep + currentRowStartRad : // edge case if there is only one seat in this row, we put it in the middle | ||
(currentRowStartRad + currentRowEndRad) / 2; // convert the angle to x y coordinates | ||
points = points.concat([{ | ||
x: Math.cos(currentAngle) * (graphRadius - rowHeight * currentRow) + graphicHeight, | ||
// flip the y coordinates | ||
y: graphicHeight - (Math.sin(currentAngle) * (graphRadius - rowHeight * currentRow) + seatRadius) // Add seatRadius and any sectionGap / 4 so that we vertically center | ||
+ seatRadius + sectionGap / 4, | ||
angle: currentAngle | ||
}]); | ||
} | ||
currentRow += 1; | ||
g.append('line') | ||
.attr('x1', radius + offsetX) | ||
.attr('y1', radius + offsetY) | ||
.attr('x2', ((Math.cos(radAngle) * radius) + radius) + offsetX) | ||
.attr('y2', (radius - (Math.sin(radAngle) * radius)) + offsetY) | ||
.attr('stroke', debugColor) | ||
.attr('stroke-width', 2) | ||
.attr('stroke-dasharray', '5 5'); | ||
} | ||
} | ||
}; | ||
return points; | ||
}; | ||
/** | ||
* Generates the list of all points and their x and y positions | ||
* | ||
* @param totalPoints - total number of points we want to draw | ||
* @param sections - total number of sections we want to draw | ||
* @param sectionGap - gap between sections | ||
* @param seatRadius - radius of each seat | ||
* @param rowHeight - height of each row | ||
* @param graphicWidth - width of the entire graphic | ||
* @returns {this} | ||
*/ | ||
/** | ||
* ___ ____ ___ _ _ _ ___ _ _ | ||
* | \__ / | _ \__ _ _ _| (_)__ _ _ __ ___ _ _| |_ / __| |_ __ _ _ _| |_ | ||
* | |) |_ \ | _/ _` | '_| | / _` | ' \/ -_) ' \ _| | (__| ' \/ _` | '_| _| | ||
* |___/___/ |_| \__,_|_| |_|_\__,_|_|_|_\___|_||_\__| \___|_||_\__,_|_| \__| | ||
* | ||
* A d3 plugin for making semi-circle parliament charts. | ||
*/ | ||
var parliamentChart = (data = [], width = 0) => { | ||
// Dimensions of the graphic | ||
let graphicWidth = parseFloat(width); | ||
var getParliamentPoints = (function (totalPoints, _ref2, graphicWidth) { | ||
var sections = _ref2.sections, | ||
sectionGap = _ref2.sectionGap, | ||
seatRadius = _ref2.seatRadius, | ||
rowHeight = _ref2.rowHeight; | ||
// Calculate the graphic height | ||
var graphicHeight = graphicWidth / 2; // Get the number of final sections | ||
// clean out any x and y values provided in data objects. | ||
let rawData = data.map(({ x, y, ...restProps }) => restProps); | ||
var finalSections = Math.min(totalPoints, sections); // Angle step per section in radians | ||
// visual options | ||
const options = { | ||
sections: 4, // Number of sections to divide the half circle into | ||
sectionGap: 60, // The gap of the aisle between sections | ||
seatRadius: 12, // The radius of each seat | ||
rowHeight: 42, // The height of each row | ||
}; | ||
var radStep = Math.PI / finalSections; // Divide the seats evenly among the sections, while also calculating | ||
// the start radians and end radians. | ||
// Whether we should draw the debug lines or not | ||
let debug = false; | ||
var sectionObjs = Array(finalSections) // First evenly divide the seats into each section | ||
.fill({ | ||
seats: Math.floor(totalPoints / finalSections) | ||
}) // add the start and end radians | ||
.map(function (a, i) { | ||
return _objectSpread2(_objectSpread2({}, a), {}, { | ||
startRad: i * radStep, | ||
endRad: (i + 1) * radStep | ||
}); | ||
}); // There are leftover seats that we need to fit into sections | ||
// Calculate how many there are | ||
// ////////////////////////////////////////////////////////////////////////// | ||
// Selection call | ||
// | ||
// This function gets called on instances such as: | ||
// d3.select('g').call(parliamentChart()) | ||
const parliamentChart = (selection) => { | ||
if (graphicWidth === 0) { | ||
// Sets the graphicWidth based on our selected container | ||
graphicWidth = selection.node().getBoundingClientRect().width; | ||
} | ||
var leftoverSeats = totalPoints % finalSections; // If leftover seats is 0, we can skip this entire section | ||
// Get the processed data (filter for entries that have x and y locations) | ||
const processedData = parliamentChart.data().filter((r) => r.x && r.y); | ||
if (leftoverSeats !== 0) { | ||
// We want to add the leftover seats to the center section first, then move outward | ||
// We do this by separating the sections into two arrays, left and right | ||
var right = Array(finalSections).fill(null).map(function (c, i) { | ||
return i; | ||
}); | ||
var left = right.splice(0, Math.floor(finalSections / 2)).reverse(); // Add the seats | ||
// Remove existing chart | ||
selection.select('g.parliament-chart').remove(); | ||
while (leftoverSeats > 0) { | ||
// Whichever array is longer, we pop from that array and add to that section first | ||
if (left.length >= right.length) sectionObjs[left.shift()].seats += 1;else sectionObjs[right.shift()].seats += 1; // decrement leftoverSeats by one | ||
// Add new chart | ||
const innerSelection = selection | ||
.append('g') | ||
.attr('class', 'parliament-chart'); | ||
leftoverSeats -= 1; | ||
} | ||
} // Call the section partial generation tool for each section | ||
// First remove any existing debug lines | ||
innerSelection.select('g.debug').remove(); | ||
// Append debug lines | ||
if (debug) debugGuides(innerSelection, graphicWidth, options, processedData.length); | ||
return sectionObjs.map(function (s) { | ||
return generatePartial(_objectSpread2(_objectSpread2({}, s), {}, { | ||
seatRadius: seatRadius, | ||
rowHeight: rowHeight, | ||
graphicHeight: graphicHeight, | ||
sectionGap: sectionGap | ||
})); | ||
}) // flatten the array | ||
.reduce(function (acc, val) { | ||
return [].concat(_toConsumableArray(acc), _toConsumableArray(val)); | ||
}, []) // sort by angle | ||
.sort(function (a, b) { | ||
return b.angle - a.angle; | ||
}) // remove angle from returned dataset | ||
.map(function (r) { | ||
var angle = r.angle, | ||
rest = _objectWithoutProperties(r, ["angle"]); | ||
return innerSelection | ||
.selectAll('circle') | ||
.data(processedData) | ||
.enter() | ||
.insert('circle') | ||
.attr('cx', (d) => d.x) | ||
.attr('cy', (d) => d.y) | ||
.attr('r', options.seatRadius) | ||
.attr('fill', (d) => d.color || '#AAA'); | ||
}; | ||
return rest; | ||
}); | ||
}); | ||
// ////////////////////////////////////////////////////////////////////////// | ||
// Getters and Setters | ||
var debugColor = '#1abc9c'; | ||
/** | ||
* Draw Debugging circle and section guides. | ||
*/ | ||
var debugGuides = (function (selection, graphicWidth, options, totalSeats) { | ||
var sections = Math.min(totalSeats, options.sections); // Create a container for our debug lines | ||
var g = selection.append('g').attr('class', 'debug').attr('transform', "translate(0, ".concat(options.sectionGap / 4, ")")); // The radius of the semicircle | ||
var radius = graphicWidth / 2; // Semicircle frame | ||
g.append('path').attr('d', "M".concat(graphicWidth, ",").concat(radius, " a1,1 0 0,0 -").concat(graphicWidth, ",0 m 0 -").concat(options.sectionGap / 2, " l ").concat(graphicWidth, " 0")).attr('stroke', debugColor).attr('stroke-width', 2).attr('stroke-dasharray', '5 5').attr('fill', 'none'); // Section borders | ||
var radStep = Math.PI / sections; | ||
for (var i = 1; i < sections; i += 1) { | ||
var radAngle = radStep * i; // If the section gap is 0 we only need to draw one line | ||
if (options.sectionGap <= 0) { | ||
g.append('line').attr('x1', radius).attr('y1', radius).attr('x2', Math.cos(radAngle) * radius + radius).attr('y2', radius - Math.sin(radAngle) * radius).attr('stroke', debugColor).attr('stroke-width', 2).attr('stroke-dasharray', '5 5'); | ||
} else { | ||
// Calculate the offset and draw two lines. | ||
var offsetX = Math.sin(radAngle) * (options.sectionGap / 2); | ||
var offsetY = Math.cos(radAngle) * (options.sectionGap / 2); | ||
g.append('line').attr('x1', radius - offsetX).attr('y1', radius - offsetY).attr('x2', Math.cos(radAngle) * radius + radius - offsetX).attr('y2', radius - Math.sin(radAngle) * radius - offsetY).attr('stroke', debugColor).attr('stroke-width', 2).attr('stroke-dasharray', '5 5'); | ||
g.append('line').attr('x1', radius + offsetX).attr('y1', radius + offsetY).attr('x2', Math.cos(radAngle) * radius + radius + offsetX).attr('y2', radius - Math.sin(radAngle) * radius + offsetY).attr('stroke', debugColor).attr('stroke-width', 2).attr('stroke-dasharray', '5 5'); | ||
} | ||
// Sets the width and the height of the graphic | ||
parliamentChart.width = (w) => { | ||
// eslint-disable-next-line no-restricted-globals | ||
if (!isNaN(w)) { | ||
// parse the width | ||
graphicWidth = parseFloat(w); | ||
} | ||
}); | ||
return parliamentChart; | ||
}; | ||
/** | ||
* ___ ____ ___ _ _ _ ___ _ _ | ||
* | \__ / | _ \__ _ _ _| (_)__ _ _ __ ___ _ _| |_ / __| |_ __ _ _ _| |_ | ||
* | |) |_ \ | _/ _` | '_| | / _` | ' \/ -_) ' \ _| | (__| ' \/ _` | '_| _| | ||
* |___/___/ |_| \__,_|_| |_|_\__,_|_|_|_\___|_||_\__| \___|_||_\__,_|_| \__| | ||
* | ||
* A d3 plugin for making semi-circle parliament charts. | ||
*/ | ||
var parliamentChart = (function () { | ||
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
var width = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
// Dimensions of the graphic | ||
var graphicWidth = parseFloat(width); // clean out any x and y values provided in data objects. | ||
var rawData = data.map(function (_ref) { | ||
var x = _ref.x, | ||
y = _ref.y, | ||
restProps = _objectWithoutProperties(_ref, ["x", "y"]); | ||
return restProps; | ||
}); // visual options | ||
var options = { | ||
sections: 4, | ||
// Number of sections to divide the half circle into | ||
sectionGap: 60, | ||
// The gap of the aisle between sections | ||
seatRadius: 12, | ||
// The radius of each seat | ||
rowHeight: 42 // The height of each row | ||
}; // Whether we should draw the debug lines or not | ||
var debug = false; // ////////////////////////////////////////////////////////////////////////// | ||
// Selection call | ||
// | ||
// This function gets called on instances such as: | ||
// d3.select('g').call(parliamentChart()) | ||
var parliamentChart = function parliamentChart(selection) { | ||
if (graphicWidth === 0) { | ||
// Sets the graphicWidth based on our selected container | ||
graphicWidth = selection.node().getBoundingClientRect().width; | ||
} // Get the processed data (filter for entries that have x and y locations) | ||
var processedData = parliamentChart.data().filter(function (r) { | ||
return r.x && r.y; | ||
}); // Remove existing chart | ||
selection.select('g.parliament-chart').remove(); // Add new chart | ||
var innerSelection = selection.append('g').attr('class', 'parliament-chart'); // First remove any existing debug lines | ||
innerSelection.select('g.debug').remove(); // Append debug lines | ||
if (debug) debugGuides(innerSelection, graphicWidth, options, processedData.length); | ||
return innerSelection.selectAll('circle').data(processedData).enter().insert('circle').attr('cx', function (d) { | ||
return d.x; | ||
}).attr('cy', function (d) { | ||
return d.y; | ||
}).attr('r', options.seatRadius).attr('fill', function (d) { | ||
return d.color || '#AAA'; | ||
}); | ||
}; // ////////////////////////////////////////////////////////////////////////// | ||
// Getters and Setters | ||
// Sets the width and the height of the graphic | ||
parliamentChart.width = function (w) { | ||
// eslint-disable-next-line no-restricted-globals | ||
if (!isNaN(w)) { | ||
// parse the width | ||
graphicWidth = parseFloat(w); | ||
} | ||
return parliamentChart; | ||
}; // Create getters and setters for sections, sectionGap, seatRadius, and rowHeight | ||
Object.keys(options).forEach(function (attr) { | ||
parliamentChart[attr] = function (s) { | ||
// Create getters and setters for sections, sectionGap, seatRadius, and rowHeight | ||
Object.keys(options) | ||
.forEach((attr) => { | ||
parliamentChart[attr] = (s) => { | ||
// eslint-disable-next-line no-restricted-globals | ||
@@ -387,72 +305,63 @@ if (!isNaN(s)) { | ||
} | ||
return options[attr]; | ||
}; | ||
}); // enable / disable debug mode | ||
}); | ||
parliamentChart.debug = function (b) { | ||
debug = !!b; | ||
// enable / disable debug mode | ||
parliamentChart.debug = (b) => { | ||
debug = !!b; | ||
return parliamentChart; | ||
}; | ||
// ////////////////////////////////////////////////////////////////////////// | ||
// Data Processing | ||
// | ||
// Gets the data processed data with x and y coordinates or sets | ||
// the raw data. | ||
parliamentChart.data = (d) => { | ||
// If an argument with new data is provided | ||
if (d) { | ||
// clean out any x and y values provided in data objects. | ||
rawData = d.map(({ x, y, ...restProps }) => restProps); | ||
return parliamentChart; | ||
}; // ////////////////////////////////////////////////////////////////////////// | ||
// Data Processing | ||
// | ||
// Gets the data processed data with x and y coordinates or sets | ||
// the raw data. | ||
} | ||
// If width is not set, don't calculate this instance | ||
if (graphicWidth <= 0 || rawData.length <= 0) return rawData; | ||
parliamentChart.data = function (d) { | ||
// If an argument with new data is provided | ||
if (d) { | ||
// clean out any x and y values provided in data objects. | ||
rawData = d.map(function (_ref2) { | ||
var x = _ref2.x, | ||
y = _ref2.y, | ||
restProps = _objectWithoutProperties(_ref2, ["x", "y"]); | ||
// Check if we have already run this instance | ||
if (rawData.every((r) => r.x && r.y)) return rawData; | ||
return restProps; | ||
}); | ||
return parliamentChart; | ||
} // If width is not set, don't calculate this instance | ||
// The number of points we need to fit | ||
const totalPoints = rawData.length; | ||
// The locations of all the points | ||
const locations = getParliamentPoints(totalPoints, options, graphicWidth); | ||
if (graphicWidth <= 0 || rawData.length <= 0) return rawData; // Check if we have already run this instance | ||
// Add locations to the rawData object | ||
locations.forEach((coords, i) => rawData[i] = ({ ...rawData[i], ...coords })); | ||
if (rawData.every(function (r) { | ||
return r.x && r.y; | ||
})) return rawData; // The number of points we need to fit | ||
// return the data | ||
return rawData; | ||
}; | ||
var totalPoints = rawData.length; // The locations of all the points | ||
// Instead of passing in an array of every single point, we pass in an array of objects | ||
// that each have a key `seats` that specifies the number of seats. This function can only | ||
// set, not get. | ||
parliamentChart.aggregatedData = (d) => { | ||
rawData = d.reduce((acc, val) => { | ||
const { seats = 0, x, y, ...restProps } = val; | ||
return [...acc, ...Array(seats).fill(restProps)]; | ||
}, []); | ||
var locations = getParliamentPoints(totalPoints, options, graphicWidth); // Add locations to the rawData object | ||
return parliamentChart; | ||
}; | ||
locations.forEach(function (coords, i) { | ||
return rawData[i] = _objectSpread2(_objectSpread2({}, rawData[i]), coords); | ||
}); // return the data | ||
return parliamentChart; | ||
}; | ||
return rawData; | ||
}; // Instead of passing in an array of every single point, we pass in an array of objects | ||
// that each have a key `seats` that specifies the number of seats. This function can only | ||
// set, not get. | ||
exports.parliamentChart = parliamentChart; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
parliamentChart.aggregatedData = function (d) { | ||
rawData = d.reduce(function (acc, val) { | ||
var _val$seats = val.seats, | ||
seats = _val$seats === void 0 ? 0 : _val$seats, | ||
x = val.x, | ||
y = val.y, | ||
restProps = _objectWithoutProperties(val, ["seats", "x", "y"]); | ||
return [].concat(_toConsumableArray(acc), _toConsumableArray(Array(seats).fill(restProps))); | ||
}, []); | ||
return parliamentChart; | ||
}; | ||
return parliamentChart; | ||
}); | ||
exports.parliamentChart = parliamentChart; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); |
@@ -1,1 +0,1 @@ | ||
!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((t="undefined"!=typeof globalThis?globalThis:t||self).d3={})}(this,function(t){"use strict";function r(r,t){var e,n=Object.keys(r);return Object.getOwnPropertySymbols&&(e=Object.getOwnPropertySymbols(r),t&&(e=e.filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})),n.push.apply(n,e)),n}function h(n){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach(function(t){var r,e;r=n,t=a[e=t],e in r?Object.defineProperty(r,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):r[e]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach(function(t){Object.defineProperty(n,t,Object.getOwnPropertyDescriptor(a,t))})}return n}function d(t,r){if(null==t)return{};var e,n=function(t,r){if(null==t)return{};for(var e,n={},a=Object.keys(t),o=0;o<a.length;o++)e=a[o],0<=r.indexOf(e)||(n[e]=t[e]);return n}(t,r);if(Object.getOwnPropertySymbols)for(var a=Object.getOwnPropertySymbols(t),o=0;o<a.length;o++)e=a[o],0<=r.indexOf(e)||Object.prototype.propertyIsEnumerable.call(t,e)&&(n[e]=t[e]);return n}function y(t){return function(t){if(Array.isArray(t))return n(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||function(t,r){if(!t)return;if("string"==typeof t)return n(t,r);var e=Object.prototype.toString.call(t).slice(8,-1);"Object"===e&&t.constructor&&(e=t.constructor.name);if("Map"===e||"Set"===e)return Array.from(t);if("Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e))return n(t,r)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function n(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=new Array(r);e<r;e++)n[e]=t[e];return n}function c(t,r,e){var n=r.sections,a=r.sectionGap,o=r.seatRadius,i=r.rowHeight,c=e/2,n=Math.min(t,n),s=Math.PI/n,u=Array(n).fill({seats:Math.floor(t/n)}).map(function(t,r){return h(h({},t),{},{startRad:r*s,endRad:(r+1)*s})}),f=t%n;if(0!==f)for(var l=Array(n).fill(null).map(function(t,r){return r}),p=l.splice(0,Math.floor(n/2)).reverse();0<f;)p.length>=l.length?u[p.shift()].seats+=1:u[l.shift()].seats+=1,--f;return u.map(function(t){return function(t){for(var r=t.seats,e=t.startRad,n=t.endRad,a=t.seatRadius,o=t.rowHeight,i=t.graphicHeight,c=t.sectionGap,s=i-a,u=[],f=0;u.length<r;){var l=s-o*f,p=Math.atan((a+c/2)/l),h=e+p,d=n-p;if(d<=h||l<=0)break;for(var l=Math.atan(2.5*a/l),y=Math.min(Math.floor((d-h)/l),r-u.length-1),g=y?(d-h)/y:0,b=0;b<=y;b+=1)var m=y?b*g+h:(h+d)/2,u=u.concat([{x:Math.cos(m)*(s-o*f)+i,y:i-(Math.sin(m)*(s-o*f)+a)+a+c/4,angle:m}]);f+=1}return u}(h(h({},t),{},{seatRadius:o,rowHeight:i,graphicHeight:c,sectionGap:a}))}).reduce(function(t,r){return[].concat(y(t),y(r))},[]).sort(function(t,r){return r.angle-t.angle}).map(function(t){t.angle;return d(t,["angle"])})}var p="#1abc9c";t.parliamentChart=function(){function e(t){0===n&&(n=t.node().getBoundingClientRect().width);var r=e.data().filter(function(t){return t.x&&t.y});return t.select("g.parliament-chart").remove(),(t=t.append("g").attr("class","parliament-chart")).select("g.debug").remove(),i&&function(t,r,e,n){var a=Math.min(n,e.sections),o=t.append("g").attr("class","debug").attr("transform","translate(0, ".concat(e.sectionGap/4,")")),i=r/2;o.append("path").attr("d","M".concat(r,",").concat(i," a1,1 0 0,0 -").concat(r,",0 m 0 -").concat(e.sectionGap/2," l ").concat(r," 0")).attr("stroke",p).attr("stroke-width",2).attr("stroke-dasharray","5 5").attr("fill","none");for(var c=Math.PI/a,s=1;s<a;s+=1){var u,f,l=c*s;e.sectionGap<=0?o.append("line").attr("x1",i).attr("y1",i).attr("x2",Math.cos(l)*i+i).attr("y2",i-Math.sin(l)*i).attr("stroke",p).attr("stroke-width",2).attr("stroke-dasharray","5 5"):(u=Math.sin(l)*(e.sectionGap/2),f=Math.cos(l)*(e.sectionGap/2),o.append("line").attr("x1",i-u).attr("y1",i-f).attr("x2",Math.cos(l)*i+i-u).attr("y2",i-Math.sin(l)*i-f).attr("stroke",p).attr("stroke-width",2).attr("stroke-dasharray","5 5"),o.append("line").attr("x1",i+u).attr("y1",i+f).attr("x2",Math.cos(l)*i+i+u).attr("y2",i-Math.sin(l)*i+f).attr("stroke",p).attr("stroke-width",2).attr("stroke-dasharray","5 5"))}}(t,n,o,r.length),t.selectAll("circle").data(r).enter().insert("circle").attr("cx",function(t){return t.x}).attr("cy",function(t){return t.y}).attr("r",o.seatRadius).attr("fill",function(t){return t.color||"#AAA"})}var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:[],n=parseFloat(1<arguments.length&&void 0!==arguments[1]?arguments[1]:0),a=t.map(function(t){t.x,t.y;return d(t,["x","y"])}),o={sections:4,sectionGap:60,seatRadius:12,rowHeight:42},i=!1;return e.width=function(t){return isNaN(t)||(n=parseFloat(t)),e},Object.keys(o).forEach(function(r){e[r]=function(t){return isNaN(t)?o[r]:(o[r]=parseInt(t,10),e)}}),e.debug=function(t){return i=!!t,e},e.data=function(t){if(t)return a=t.map(function(t){t.x,t.y;return d(t,["x","y"])}),e;if(n<=0||a.length<=0)return a;if(a.every(function(t){return t.x&&t.y}))return a;t=a.length;return c(t,o,n).forEach(function(t,r){return a[r]=h(h({},a[r]),t)}),a},e.aggregatedData=function(t){return a=t.reduce(function(t,r){var e=r.seats,e=void 0===e?0:e,r=(r.x,r.y,d(r,["seats","x","y"]));return[].concat(y(t),y(Array(e).fill(r)))},[]),e},e},Object.defineProperty(t,"__esModule",{value:!0})}); | ||
!function(t,a){"object"==typeof exports&&"undefined"!=typeof module?a(exports):"function"==typeof define&&define.amd?define(["exports"],a):a((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,function(t){"use strict";const p=({seats:t,startRad:a,endRad:e,seatRadius:r,rowHeight:s,graphicHeight:n,sectionGap:o})=>{var i=n-r;let l=[],c=0;for(;l.length<t;){var h=i-s*c,d=Math.atan((r+o/2)/h),p=a+d,f=e-d;if(f<=p||h<=0)break;var d=Math.atan(2.5*r/h),g=Math.min(Math.floor((f-p)/d),t-l.length-1),u=g?(f-p)/g:0;for(let t=0;t<=g;t+=1){var y=g?t*u+p:(p+f)/2;l=l.concat([{x:Math.cos(y)*(i-s*c)+n,y:n-(Math.sin(y)*(i-s*c)+r)+r+o/4,angle:y}])}c+=1}return l};const M="#1abc9c";t.parliamentChart=(t=[],a=0)=>{let f=parseFloat(a),e=t.map(({x:t,y:a,...e})=>e);const g={sections:4,sectionGap:60,seatRadius:12,rowHeight:42};let u=!1;const y=t=>{0===f&&(f=t.node().getBoundingClientRect().width);var a=y.data().filter(t=>t.x&&t.y);t.select("g.parliament-chart").remove();const e=t.append("g").attr("class","parliament-chart");if(e.select("g.debug").remove(),u){var t=e,r=f,s=g,n=a.length,o=Math.min(n,s.sections);const p=t.append("g").attr("class","debug").attr("transform",`translate(0, ${s.sectionGap/4})`);var i=r/2,l=(p.append("path").attr("d",`M${r},${i} a1,1 0 0,0 -${r},0 m 0 -${s.sectionGap/2} l ${r} 0`).attr("stroke",M).attr("stroke-width",2).attr("stroke-dasharray","5 5").attr("fill","none"),Math.PI/o);for(let t=1;t<o;t+=1){var c,h,d=l*t;s.sectionGap<=0?p.append("line").attr("x1",i).attr("y1",i).attr("x2",Math.cos(d)*i+i).attr("y2",i-Math.sin(d)*i).attr("stroke",M).attr("stroke-width",2).attr("stroke-dasharray","5 5"):(c=Math.sin(d)*(s.sectionGap/2),h=Math.cos(d)*(s.sectionGap/2),p.append("line").attr("x1",i-c).attr("y1",i-h).attr("x2",Math.cos(d)*i+i-c).attr("y2",i-Math.sin(d)*i-h).attr("stroke",M).attr("stroke-width",2).attr("stroke-dasharray","5 5"),p.append("line").attr("x1",i+c).attr("y1",i+h).attr("x2",Math.cos(d)*i+i+c).attr("y2",i-Math.sin(d)*i+h).attr("stroke",M).attr("stroke-width",2).attr("stroke-dasharray","5 5"))}}return e.selectAll("circle").data(a).enter().insert("circle").attr("cx",t=>t.x).attr("cy",t=>t.y).attr("r",g.seatRadius).attr("fill",t=>t.color||"#AAA")};return y.width=t=>(isNaN(t)||(f=parseFloat(t)),y),Object.keys(g).forEach(a=>{y[a]=t=>isNaN(t)?g[a]:(g[a]=parseInt(t,10),y)}),y.debug=t=>(u=!!t,y),y.data=t=>{if(t)return e=t.map(({x:t,y:a,...e})=>e),y;if(f<=0||e.length<=0)return e;if(e.every(t=>t.x&&t.y))return e;const a=((t,{sections:a,sectionGap:e,seatRadius:r,rowHeight:s},n)=>{const o=n/2;n=Math.min(t,a);const i=Math.PI/n,l=Array(n).fill({seats:Math.floor(t/n)}).map((t,a)=>({...t,startRad:a*i,endRad:(a+1)*i}));let c=t%n;if(0!==c){const h=Array(n).fill(null).map((t,a)=>a),d=h.splice(0,Math.floor(n/2)).reverse();for(;0<c;)d.length>=h.length?l[d.shift()].seats+=1:l[h.shift()].seats+=1,--c}return l.map(t=>p({...t,seatRadius:r,rowHeight:s,graphicHeight:o,sectionGap:e})).reduce((t,a)=>[...t,...a],[]).sort((t,a)=>a.angle-t.angle).map(t=>{const{angle:a,...e}=t;return e})})(e.length,g,f);return a.forEach((t,a)=>e[a]={...e[a],...t}),e},y.aggregatedData=t=>(e=t.reduce((t,a)=>{const{seats:e=0,x:r,y:s,...n}=a;return[...t,...Array(e).fill(n)]},[]),y),y},Object.defineProperty(t,"__esModule",{value:!0})}); |
{ | ||
"name": "d3-parliament-chart", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "parliament / congress / legislature charts simplified", | ||
@@ -15,4 +15,3 @@ "main": "build/d3-parliament-chart.js", | ||
"test": "jest", | ||
"build": "npm run clean && npm run build:umd", | ||
"build:umd": "rollup -c -o build/d3-parliament-chart.js -f umd -n d3", | ||
"build": "npm run clean && rollup -c", | ||
"prepublishOnly": "npm run build && uglifyjs build/d3-parliament-chart.js -c -m -o build/d3-parliament-chart.min.js", | ||
@@ -46,3 +45,3 @@ "postpublish": "zip -j build/d3-parliament-chart.zip -- LICENSE README.md build/d3-parliament-chart.js build/d3-parliament-chart.min.js" | ||
"babel-jest": "^26.6.3", | ||
"d3": "^6.3.1", | ||
"d3": "^7.4.4", | ||
"eslint": "^7.16.0", | ||
@@ -59,4 +58,4 @@ "eslint-config-airbnb-base": "^14.2.1", | ||
"svelte": "^3.31.2", | ||
"uglify-js": "^3.12.4" | ||
"uglify-js": "^3.15.4" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21975
308
1