Skip to content

Commit

Permalink
Merge branch 'dev' into feature/thumb-no-click
Browse files Browse the repository at this point in the history
  • Loading branch information
brauliodiez authored Nov 17, 2024
2 parents 999a0e1 + 1aa807b commit b0e9692
Show file tree
Hide file tree
Showing 8 changed files with 441 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const ButtonShape = forwardRef<any, ShapeProps>((props, ref) => {
/>
<Text
x={0}
y={restrictedHeight / 2 - 5}
y={(restrictedHeight - 15) / 2}
width={restrictedWidth}
height={restrictedHeight - restrictedHeight / 2 - 5}
text={text}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { describe, it, expect } from 'vitest';
import { balanceSpacePerItem } from './balance-space';

const _sum = (resultado: number[]) =>
resultado.reduce((acc, current) => acc + current, 0);

describe('balanceSpacePerItem tests', () => {
it('should return an array which sums 150 when apply [10, 20, 30, 40, 50]', () => {
// Arrange
const theArray = [10, 20, 30, 40, 50];
const availableWidth = 150;

// Act
const result = balanceSpacePerItem(theArray, availableWidth);
const totalSum = _sum(result);

// Assert
expect(totalSum).toBeGreaterThan(0);
expect(totalSum).toBeLessThanOrEqual(availableWidth);
});

it('should return an array which sums equal or less than 100 when apply [10, 20, 30, 40, 50]', () => {
// Arrange
const theArray = [10, 20, 30, 40, 50];
const availableWidth = 100;

// Act
const result = balanceSpacePerItem(theArray, availableWidth);
const totalSum = _sum(result);

// Assert
expect(totalSum).toBeGreaterThan(0);
expect(totalSum).toBeLessThanOrEqual(availableWidth);
});

it('should return an array which sums less or equal than 150 when apply [10, 20, 31, 41, 50]', () => {
// Arrange
const theArray = [10, 20, 31, 41, 50];
const availableWidth = 150;

// Act
const result = balanceSpacePerItem(theArray, availableWidth);
const totalSum = _sum(result);

// Assert
expect(totalSum).toBeGreaterThan(0);
expect(totalSum).toBeLessThanOrEqual(availableWidth);
});

it('should return an array which sums 10 when apply [10]', () => {
// Arrange
const theArray = [100];
const availableWidth = 10;

// Act
const result = balanceSpacePerItem(theArray, availableWidth);
const totalSum = _sum(result);

// Assert
expect(totalSum).toBeGreaterThan(0);
expect(totalSum).toBeLessThanOrEqual(availableWidth);
});

it('should return an array which sums 18 when apply [10, 10]', () => {
// Arrange
const theArray = [10, 10];
const availableWidth = 18;

// Act
const result = balanceSpacePerItem(theArray, availableWidth);
const totalSum = _sum(result);

// Assert
expect(totalSum).toBeGreaterThan(0);
expect(totalSum).toBeLessThanOrEqual(availableWidth);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* This calc is made "layer by layer", distributing a larger chunk of width in each iteration
* @param {Array} itemList - List of spaces to balance (Must be provided in ascendent order to work)
* @param {Number} availableSpace - The amount of space to be distributed
*/
export const balanceSpacePerItem = (
itemList: number[],
availableSpace: number
) => {
const totalSpaceUsed = _spacesFactory();
const maxItemSize = _spacesFactory();

return itemList.reduce((newList: number[], current, index, arr) => {
// Check if the array provided is properly ordered
if (index > 0) _checkListOrder(arr[index - 1], current);

const lastItemSize: number = index > 0 ? newList[index - 1] : 0;

// A) Once the maximum possible size of the item is reached, apply this size directly.
if (maxItemSize.value) {
totalSpaceUsed.add(maxItemSize.value);
return [...newList, lastItemSize];
}

/** Precalculate "existingSum + spaceSum" taking into account
* all next items supposing all they use current size */
const timesToApply = arr.length - index;
const virtualTotalsSum = totalSpaceUsed.value + current * timesToApply;

/** B) First "Bigger" tab behaviour: If the virtual-sum of next items using this size
* doesn't fit within available space, calc maxItemSize */
if (virtualTotalsSum >= availableSpace) {
const remainder =
availableSpace - (totalSpaceUsed.value + lastItemSize * timesToApply);
const remainderPortionPerItem = Math.floor(remainder / timesToApply);
maxItemSize.set(lastItemSize + remainderPortionPerItem);

totalSpaceUsed.add(maxItemSize.value);

return [...newList, maxItemSize.value];
}

// C) "Normal" behaviour: Apply proposed new size to current
totalSpaceUsed.add(current);
return [...newList, current];
}, []);
};

/* Balance helper functions: */

function _checkListOrder(prev: number, current: number) {
if (prev > current) {
throw new Error(
'Disordered list. Please provide an ascendent ordered list as param *itemlist*'
);
}
}

function _spacesFactory() {
let _size = 0;
//Assure we are setting natural num w/o decimals
const _adjustNum = (num: number) => {
if (typeof num !== 'number') throw new Error('Number must be provided');
return Math.max(0, Math.floor(num));
};
const add = (qty: number) => (_size += _adjustNum(qty));
const set = (qty: number) => (_size = _adjustNum(qty));
return {
get value() {
return _size;
},
add,
set,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Layer } from 'konva/lib/Layer';

/**
* Virtually calculates the width that a text will occupy, by using a canvas.
* If a Konva Layer is provided, it will reuse the already existing canvas.
* Otherwise, it will create a canvas within the document, on the fly, to perform the measurement.
* Finaly, as a safety net, a very generic calculation is provided in case the other options are not available.
*/
export const calcTextWidth = (
inputText: string,
fontSize: number,
fontfamily: string,
konvaLayer?: Layer
) => {
if (konvaLayer)
return _getTextWidthByKonvaMethod(
konvaLayer,
inputText,
fontSize,
fontfamily
);

return _getTextCreatingNewCanvas(inputText, fontSize, fontfamily);
};

const _getTextWidthByKonvaMethod = (
konvaLayer: Layer,
text: string,
fontSize: number,
fontfamily: string
) => {
const context = konvaLayer.getContext();
context.font = `${fontSize}px ${fontfamily}`;
return context.measureText(text).width;
};

const _getTextCreatingNewCanvas = (
text: string,
fontSize: number,
fontfamily: string
) => {
let canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (context) {
context.font = `${fontSize}px ${fontfamily}`;
return context.measureText(text).width;
}
const charAverageWidth = fontSize * 0.7;
return text.length * charAverageWidth + charAverageWidth * 0.8;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, it, expect } from 'vitest';
import { adjustTabWidths } from './tabsbar.business';

const _sum = (resultado: number[]) =>
resultado.reduce((acc, current) => acc + current, 0);

describe('tabsbar.business tests', () => {
it('should return a new array of numbers, which sum is less than or equal to totalWidth', () => {
// Arrange
const tabs = [
'Text',
'Normal text for tab',
'Extra large text for a tab',
'Really really large text for a tab',
'xs',
];
const containerWidth = 1000;
const minTabWidth = 100;
const tabsGap = 10;

// Act
const result = adjustTabWidths({
tabs,
containerWidth,
minTabWidth,
tabXPadding: 20,
tabsGap,
font: { fontSize: 14, fontFamily: 'Arial' },
});

console.log({ tabs }, { containerWidth }, { minTabWidth });
console.log({ result });

const totalSum = _sum(result.widthList) + (tabs.length - 1) * tabsGap;
console.log('totalSum: ', totalSum);

// Assert
expect(result.widthList[0]).not.toBe(0);
expect(result.widthList.length).toBe(tabs.length);
expect(totalSum).toBeLessThanOrEqual(containerWidth);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Layer } from 'konva/lib/Layer';
import { balanceSpacePerItem } from './balance-space';
import { calcTextWidth } from './calc-text-width';

export const adjustTabWidths = (args: {
tabs: string[];
containerWidth: number;
minTabWidth: number;
tabXPadding: number;
tabsGap: number;
font: {
fontSize: number;
fontFamily: string;
};
konvaLayer?: Layer;
}) => {
const {
tabs,
containerWidth,
minTabWidth,
tabXPadding,
tabsGap,
font,
konvaLayer,
} = args;
const totalInnerXPadding = tabXPadding * 2;
const totalMinTabSpace = minTabWidth + totalInnerXPadding;
const containerWidthWithoutTabGaps =
containerWidth - (tabs.length - 1) * tabsGap;

//Create info List with originalPositions and desired width
interface OriginalTabInfo {
originalTabPosition: number;
desiredWidth: number;
}
const arrangeTabsInfo = tabs.reduce(
(acc: OriginalTabInfo[], tab, index): OriginalTabInfo[] => {
const tabFullTextWidth =
calcTextWidth(tab, font.fontSize, font.fontFamily, konvaLayer) +
totalInnerXPadding;
const desiredWidth = Math.max(totalMinTabSpace, tabFullTextWidth);
return [
...acc,
{
originalTabPosition: index,
desiredWidth,
},
];
},
[]
);

// This order is necessary to build layer by layer the new sizes
const ascendentTabList = arrangeTabsInfo.sort(
(a, b) => a.desiredWidth - b.desiredWidth
);

const onlyWidthList = ascendentTabList.map(tab => tab.desiredWidth);
// Apply adjustments
const adjustedSizeList = balanceSpacePerItem(
onlyWidthList,
containerWidthWithoutTabGaps
);

// Reassemble new data with the original order
const reassembledData = ascendentTabList.reduce(
(accList: number[], current, index) => {
const newList = [...accList];
newList[current.originalTabPosition] = adjustedSizeList[index];
return newList;
},
[]
);

// Calc item offset position (mixed with external variable to avoid adding to reducer() extra complexity)
let sumOfXposition = 0;
const relativeTabPosition = reassembledData.reduce(
(acc: number[], currentTab, index) => {
const currentElementXPos = index ? sumOfXposition : 0;
sumOfXposition += currentTab + tabsGap;
return [...acc, currentElementXPos];
},
[]
);

return { widthList: reassembledData, relativeTabPosition };
};
Loading

0 comments on commit b0e9692

Please sign in to comment.