From 8f55046e04971da31654aa7adeb07ea692655f69 Mon Sep 17 00:00:00 2001 From: Florian Guitton <2495155+fguitton@users.noreply.github.com> Date: Mon, 22 Jun 2020 16:51:02 +0100 Subject: [PATCH] feat(calendar): add monthSpacing prop (#964) * Add month spacing to @nivo/calendar * feat(website): Add daySpacing to calendar component * fix(calendar): Add monthSpacing to types Co-authored-by: Neil Kistner --- packages/calendar/index.d.ts | 1 + packages/calendar/src/compute.js | 55 +++++++++++++++---- packages/calendar/src/enhance.js | 15 ++++- packages/calendar/src/props.js | 2 + packages/calendar/stories/calendar.stories.js | 2 + website/src/data/components/calendar/props.js | 15 +++++ website/src/pages/calendar/canvas.js | 1 + website/src/pages/calendar/index.js | 1 + 8 files changed, 78 insertions(+), 14 deletions(-) diff --git a/packages/calendar/index.d.ts b/packages/calendar/index.d.ts index 310cd1fac4..3ffee6513d 100644 --- a/packages/calendar/index.d.ts +++ b/packages/calendar/index.d.ts @@ -46,6 +46,7 @@ declare module '@nivo/calendar' { yearLegendOffset: number monthLegend: (year: number, month: number, date: Date) => string | number + monthSpacing: number monthBorderWidth: number monthBorderColor: string monthLegendOffset: number diff --git a/packages/calendar/src/compute.js b/packages/calendar/src/compute.js index 556683f39c..fb26eb1752 100644 --- a/packages/calendar/src/compute.js +++ b/packages/calendar/src/compute.js @@ -37,6 +37,7 @@ export const computeDomain = (data, minSpec, maxSpec) => { * @param {number} direction * @param {array} yearRange * @param {number} yearSpacing + * @param {number} monthSpacing * @param {number} daySpacing * @param {number} maxWeeks * @returns {number} @@ -47,6 +48,7 @@ const computeCellSize = ({ direction, yearRange, yearSpacing, + monthSpacing, daySpacing, maxWeeks, }) => { @@ -54,7 +56,7 @@ const computeCellSize = ({ let vCellSize if (direction === 'horizontal') { - hCellSize = (width - daySpacing * maxWeeks) / maxWeeks + hCellSize = (width - monthSpacing * 12 - daySpacing * maxWeeks) / maxWeeks vCellSize = (height - (yearRange.length - 1) * yearSpacing - yearRange.length * (8 * daySpacing)) / (yearRange.length * 7) @@ -62,7 +64,7 @@ const computeCellSize = ({ hCellSize = (width - (yearRange.length - 1) * yearSpacing - yearRange.length * (8 * daySpacing)) / (yearRange.length * 7) - vCellSize = (height - daySpacing * maxWeeks) / maxWeeks + vCellSize = (height - monthSpacing * 12 - daySpacing * maxWeeks) / maxWeeks } return Math.min(hCellSize, vCellSize) @@ -75,6 +77,7 @@ const computeCellSize = ({ * @param {number} cellSize * @param {number} yearIndex * @param {number} yearSpacing + * @param {number} monthSpacing * @param {number} daySpacing * @param {string} direction * @param {number} originX @@ -86,6 +89,7 @@ const monthPathAndBBox = ({ cellSize, yearIndex, yearSpacing, + monthSpacing, daySpacing, direction, originX, @@ -100,13 +104,16 @@ const monthPathAndBBox = ({ const firstDay = date.getDay() const lastDay = t1.getDay() - // offset according to year index + // offset according to year index and month let xO = originX let yO = originY const yearOffset = yearIndex * (7 * (cellSize + daySpacing) + yearSpacing) + const monthOffset = date.getMonth() * monthSpacing if (direction === 'horizontal') { yO += yearOffset + xO += monthOffset } else { + yO += monthOffset xO += yearOffset } @@ -154,8 +161,18 @@ const monthPathAndBBox = ({ */ const memoMonthPathAndBBox = memoize( monthPathAndBBox, - ({ date, cellSize, yearIndex, yearSpacing, daySpacing, direction, originX, originY }) => { - return `${date.toString()}.${cellSize}.${yearIndex}.${yearSpacing}.${daySpacing}.${direction}.${originX}.${originY}` + ({ + date, + cellSize, + yearIndex, + yearSpacing, + monthSpacing, + daySpacing, + direction, + originX, + originY, + }) => { + return `${date.toString()}.${cellSize}.${yearIndex}.${yearSpacing}.${monthSpacing}.${daySpacing}.${direction}.${originX}.${originY}` } ) @@ -164,15 +181,20 @@ const memoMonthPathAndBBox = memoize( * * @param {number} cellSize * @param {number} yearSpacing + * @param {number} monthSpacing * @param {number} daySpacing * @returns { function(): { x: number, y: number } } */ -const cellPositionHorizontal = (cellSize, yearSpacing, daySpacing) => { +const cellPositionHorizontal = (cellSize, yearSpacing, monthSpacing, daySpacing) => { return (originX, originY, d, yearIndex) => { const weekOfYear = timeWeek.count(timeYear(d), d) return { - x: originX + weekOfYear * (cellSize + daySpacing) + daySpacing / 2, + x: + originX + + weekOfYear * (cellSize + daySpacing) + + daySpacing / 2 + + d.getMonth() * monthSpacing, y: originY + d.getDay() * (cellSize + daySpacing) + @@ -187,10 +209,11 @@ const cellPositionHorizontal = (cellSize, yearSpacing, daySpacing) => { * * @param {number} cellSize * @param {number} yearSpacing + * @param {number} monthSpacing * @param {number} daySpacing * @returns { function(): { x: number, y: number } } */ -const cellPositionVertical = (cellSize, yearSpacing, daySpacing) => { +const cellPositionVertical = (cellSize, yearSpacing, monthSpacing, daySpacing) => { return (originX, originY, d, yearIndex) => { const weekOfYear = timeWeek.count(timeYear(d), d) @@ -200,7 +223,11 @@ const cellPositionVertical = (cellSize, yearSpacing, daySpacing) => { d.getDay() * (cellSize + daySpacing) + daySpacing / 2 + yearIndex * (yearSpacing + 7 * (cellSize + daySpacing)), - y: originY + weekOfYear * (cellSize + daySpacing) + daySpacing / 2, + y: + originY + + weekOfYear * (cellSize + daySpacing) + + daySpacing / 2 + + d.getMonth() * monthSpacing, } } } @@ -217,6 +244,7 @@ const dayFormat = timeFormat('%Y-%m-%d') * @param {string|Date} to * @param {string} direction * @param {number} yearSpacing + * @param {number} monthSpacing * @param {number} daySpacing * @param {string} align * @returns {object} @@ -228,6 +256,7 @@ export const computeLayout = ({ to, direction, yearSpacing, + monthSpacing, daySpacing, align, }) => { @@ -248,11 +277,12 @@ export const computeLayout = ({ direction, yearRange, yearSpacing, + monthSpacing, daySpacing, maxWeeks, }) - const monthsSize = cellSize * maxWeeks + daySpacing * maxWeeks + const monthsSize = cellSize * maxWeeks + daySpacing * maxWeeks + monthSpacing * 12 const yearsSize = (cellSize + daySpacing) * 7 * yearRange.length + yearSpacing * (yearRange.length - 1) @@ -276,9 +306,9 @@ export const computeLayout = ({ let cellPosition if (direction === 'horizontal') { - cellPosition = cellPositionHorizontal(cellSize, yearSpacing, daySpacing) + cellPosition = cellPositionHorizontal(cellSize, yearSpacing, monthSpacing, daySpacing) } else { - cellPosition = cellPositionVertical(cellSize, yearSpacing, daySpacing) + cellPosition = cellPositionVertical(cellSize, yearSpacing, monthSpacing, daySpacing) } let years = [] @@ -311,6 +341,7 @@ export const computeLayout = ({ direction, yearIndex: i, yearSpacing, + monthSpacing, daySpacing, cellSize, }), diff --git a/packages/calendar/src/enhance.js b/packages/calendar/src/enhance.js index a651edde85..1b5e1fdff9 100644 --- a/packages/calendar/src/enhance.js +++ b/packages/calendar/src/enhance.js @@ -33,8 +33,18 @@ const commonEnhancers = [ } ), withPropsOnChange( - ['width', 'height', 'from', 'to', 'direction', 'yearSpacing', 'daySpacing', 'align'], - ({ width, height, from, to, direction, yearSpacing, daySpacing, align }) => { + [ + 'width', + 'height', + 'from', + 'to', + 'direction', + 'yearSpacing', + 'monthSpacing', + 'daySpacing', + 'align', + ], + ({ width, height, from, to, direction, yearSpacing, monthSpacing, daySpacing, align }) => { return computeLayout({ width, height, @@ -42,6 +52,7 @@ const commonEnhancers = [ to, direction, yearSpacing, + monthSpacing, daySpacing, align, }) diff --git a/packages/calendar/src/props.js b/packages/calendar/src/props.js index 196ebdb3a8..3fac3cacfa 100644 --- a/packages/calendar/src/props.js +++ b/packages/calendar/src/props.js @@ -46,6 +46,7 @@ const commonPropTypes = { monthBorderWidth: PropTypes.number.isRequired, monthBorderColor: PropTypes.string.isRequired, monthLegend: PropTypes.func.isRequired, + monthSpacing: PropTypes.number.isRequired, monthLegendPosition: PropTypes.oneOf(['before', 'after']).isRequired, monthLegendOffset: PropTypes.number.isRequired, @@ -90,6 +91,7 @@ const commonDefaultProps = { monthBorderWidth: 2, monthBorderColor: '#000', + monthSpacing: 0, monthLegend: (year, month, date) => monthLabelFormat(date), monthLegendPosition: 'before', monthLegendOffset: 10, diff --git a/packages/calendar/stories/calendar.stories.js b/packages/calendar/stories/calendar.stories.js index 4d9d02701b..0005c31f8f 100644 --- a/packages/calendar/stories/calendar.stories.js +++ b/packages/calendar/stories/calendar.stories.js @@ -68,3 +68,5 @@ stories.add('custom tooltip', () => ( {...commonProps} /> )) + +stories.add('month spacing', () => ) diff --git a/website/src/data/components/calendar/props.js b/website/src/data/components/calendar/props.js index a9b4565222..6281681e42 100644 --- a/website/src/data/components/calendar/props.js +++ b/website/src/data/components/calendar/props.js @@ -252,6 +252,21 @@ const props = [ }, }, // Months + { + key: 'monthSpacing', + help: 'define spacing between each month row/column depending on the direction.', + type: 'number', + required: false, + defaultValue: defaults.monthSpacing, + controlType: 'range', + group: 'Months', + controlOptions: { + unit: 'px', + min: 0, + max: 160, + step: 5, + }, + }, { key: 'monthBorderWidth', flavors: ['svg', 'api'], diff --git a/website/src/pages/calendar/canvas.js b/website/src/pages/calendar/canvas.js index 27cf918cd4..169e95ce92 100644 --- a/website/src/pages/calendar/canvas.js +++ b/website/src/pages/calendar/canvas.js @@ -47,6 +47,7 @@ const initialProperties = { yearLegendPosition: 'before', yearLegendOffset: 10, + monthSpacing: 0, monthBorderWidth: 2, monthBorderColor: '#ffffff', monthLegendPosition: 'before', diff --git a/website/src/pages/calendar/index.js b/website/src/pages/calendar/index.js index e96452ae44..2ca0435cc4 100644 --- a/website/src/pages/calendar/index.js +++ b/website/src/pages/calendar/index.js @@ -44,6 +44,7 @@ const initialProperties = { yearLegendPosition: 'before', yearLegendOffset: 10, + monthSpacing: 0, monthBorderWidth: 2, monthBorderColor: '#ffffff', monthLegendPosition: 'before',