Initial commit

Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com>
Co-Authored-By: Johan  <4934465+joolsus@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
Michael Cartner
2024-01-26 11:52:55 +01:00
commit b9c60b07dc
2789 changed files with 868795 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
import React, { useRef, useState, useEffect } from 'react';
import { ForEachComponent } from '../../../nodes/std-library/data/foreach';
import { Noodl, Slot } from '../../../types';
export interface ColumnsProps extends Noodl.ReactProps {
marginX: string;
marginY: string;
justifyContent: 'flex-start' | 'flex-end' | 'center';
direction: 'row' | 'column';
minWidth: string;
layoutString: string;
children: Slot;
}
function calcAutofold(layout, minWidths, containerWidth, marginX) {
const first = _calcAutofold(layout, minWidths, containerWidth, marginX);
const second = _calcAutofold(first.layout, minWidths, containerWidth, marginX);
if (first.totalFractions === second.totalFractions) {
return first;
} else {
return calcAutofold(second.layout, minWidths, containerWidth, marginX);
}
}
function _calcAutofold(layout, minWidth, containerWidth, marginX) {
const totalFractions = layout.reduce((a, b) => a + b, 0);
const fractionSize = 100 / totalFractions;
const rowWidth = layout.reduce(
(acc, curr, i) => {
return {
expected: acc.expected + (fractionSize / 100) * containerWidth * curr,
min: acc.min + parseFloat(minWidth) + marginX
};
},
{ expected: 0, min: 0, max: null }
);
const newLayout = layout;
if (rowWidth.expected < rowWidth.min) {
newLayout.pop();
}
const newTotalFractions = newLayout.reduce((a, b) => a + b, 0);
const newFractionSize = 100 / newTotalFractions;
const newColumnAmount = newLayout.length;
return {
layout: newLayout,
totalFractions: newTotalFractions,
fractionSize: newFractionSize,
columnAmount: newColumnAmount
};
}
export function Columns(props: ColumnsProps) {
if (!props.children) return null;
let columnLayout = null;
const containerRef = useRef(null);
const [containerWidth, setContainerWidth] = useState(null);
useEffect(() => {
const container = containerRef?.current;
if (!container) return;
const observer = new ResizeObserver(() => {
const container = containerRef.current;
if (!container) return;
setContainerWidth(container.offsetWidth);
});
observer.observe(container);
return () => {
observer.disconnect();
};
}, []);
switch (typeof props.layoutString) {
case 'string':
columnLayout = props.layoutString.trim();
break;
case 'number':
columnLayout = String(props.layoutString).trim();
break;
default:
columnLayout = null;
}
if (!columnLayout) {
return <>{props.children}</>;
}
// all data for childrens width calculation
const targetLayout = columnLayout.split(' ').map((number) => parseInt(number));
// constraints
const { layout, columnAmount, fractionSize } = calcAutofold(
targetLayout,
props.minWidth,
containerWidth,
props.marginX
);
let children = [];
let forEachComponent = null;
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
if (!Array.isArray(props.children)) {
children = [props.children];
} else {
children = props.children.filter((child) => child.type !== ForEachComponent);
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
}
return (
<div
className={['columns-container', props.className].join(' ')}
ref={containerRef}
style={{
marginTop: parseFloat(props.marginY) * -1,
marginLeft: parseFloat(props.marginX) * -1,
display: 'flex',
flexWrap: 'wrap',
alignItems: 'stretch',
justifyContent: props.justifyContent,
flexDirection: props.direction,
width: `calc(100% + (${parseFloat(props.marginX)}px)`,
boxSizing: 'border-box',
...props.style
}}
>
{forEachComponent && forEachComponent}
{children.map((child, i) => {
return (
<div
className="column-item"
key={i}
style={{
boxSizing: 'border-box',
paddingTop: props.marginY,
paddingLeft: props.marginX,
width: layout[i % columnAmount] * fractionSize + '%',
flexShrink: 0,
flexGrow: 0,
minWidth: props.minWidth
// maxWidths needs some more thought
//maxWidth: getMinMaxInputValues(maxWidths, columnAmount, props.marginX, i)
}}
>
{React.cloneElement(child)}
</div>
);
})}
</div>
);
}