@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
161 lines (154 loc) • 7.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
// Using plain React Native styles (nativewind not installed in this repo)
/**
* Responsive justified photo grid that stretches to the provided containerWidth.
* Uses flex rows with proportional children widths instead of absolute pixel widths so it always fills.
*/
const JustifiedPhotoGrid = ({
photos,
photoDimensions,
loadPhotoDimensions,
createJustifiedRows,
renderJustifiedPhotoItem,
textColor,
containerWidth: explicitWidth,
gap = 4,
minRowHeight = 100,
maxRowHeight = 300,
dateFormatLocale = 'en-US'
}) => {
// Responsive width measurement if not explicitly provided
const [measuredWidth, setMeasuredWidth] = (0, _react.useState)(null);
const effectiveWidth = explicitWidth ?? measuredWidth ?? 0; // 0 until measured
const onLayoutContainer = (0, _react.useCallback)(e => {
if (explicitWidth) return; // ignore if controlled
const w = e.nativeEvent.layout.width;
setMeasuredWidth(prev => prev === w ? prev : w);
}, [explicitWidth]);
// Ensure dimensions are loaded for displayed photos
(0, _react.useEffect)(() => {
loadPhotoDimensions(photos);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [photos.map(p => p.id).join(',')]);
// Group photos by date first
const photosByDate = (0, _react.useMemo)(() => {
return photos.reduce((groups, photo) => {
const date = new Date(photo.uploadDate).toDateString();
if (!groups[date]) groups[date] = [];
groups[date].push(photo);
return groups;
}, {});
}, [photos]);
const sortedDates = (0, _react.useMemo)(() => Object.keys(photosByDate).sort((a, b) => new Date(b).getTime() - new Date(a).getTime()), [photosByDate]);
// Track measured width of each date section (may differ if parent applies horizontal padding/margins)
const [dateWidths, setDateWidths] = (0, _react.useState)({});
const onLayoutDate = (0, _react.useCallback)((date, width) => {
setDateWidths(prev => prev[date] === width ? prev : {
...prev,
[date]: width
});
}, []);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: {
width: '100%'
},
onLayout: onLayoutContainer,
children: effectiveWidth === 0 && !explicitWidth ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
children: sortedDates.map(date => {
const dayPhotos = photosByDate[date];
// createJustifiedRows should build rows such that the "ideal" height (availableWidth / totalAspect) stays within min/max.
// We pass the effective container width.
const dateWidth = dateWidths[date] ?? effectiveWidth; // fallback to overall width until measured
const rows = dateWidth > 0 ? createJustifiedRows(dayPhotos, dateWidth) : [];
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: {
marginBottom: 24,
width: '100%'
},
onLayout: e => onLayoutDate(date, e.nativeEvent.layout.width),
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: {
fontSize: 14,
fontWeight: '600',
marginBottom: 12,
color: textColor
},
children: new Date(date).toLocaleDateString(dateFormatLocale, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: {
width: '100%'
},
children: rows.map((row, rowIndex) => {
// Compute total aspect ratios using loaded dimensions (fallback 4/3)
const aspects = row.map(p => {
const dims = photoDimensions[p.id];
return dims ? dims.width / dims.height : 4 / 3;
});
const totalAspect = aspects.reduce((a, b) => a + b, 0);
const gapsTotal = gap * (row.length - 1);
const availableWidth = dateWidth - gapsTotal;
// Ideal height that perfectly fills width when preserving aspect ratios
const idealHeight = availableWidth / totalAspect;
// We rely on row construction keeping idealHeight within min/max bounds; if not, clamp but then distribute leftover/overflow.
let rowHeight = idealHeight;
let widthAdjustment = 0; // difference to distribute if clamped
if (idealHeight < minRowHeight) {
rowHeight = minRowHeight;
widthAdjustment = availableWidth - rowHeight * totalAspect; // negative means overflow
} else if (idealHeight > maxRowHeight) {
rowHeight = maxRowHeight;
widthAdjustment = availableWidth - rowHeight * totalAspect;
}
// Pre-compute widths maintaining aspect ratios
let widths = aspects.map(ar => ar * rowHeight);
// If we have widthAdjustment (due to clamping) distribute proportionally so row still fills exactly
if (widthAdjustment !== 0) {
const widthSum = widths.reduce((a, b) => a + b, 0);
widths = widths.map(w => w + w / widthSum * widthAdjustment);
}
// To combat rounding issues, adjust last item width to fill precisely
const widthSumRounded = widths.reduce((a, b) => a + b, 0);
const roundingDiff = availableWidth - widthSumRounded;
if (Math.abs(roundingDiff) > 0.5) {
widths[widths.length - 1] += roundingDiff; // minimal correction
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: {
flexDirection: 'row',
width: '100%',
marginBottom: 4
},
children: row.map((p, i) => {
const photoWidth = widths[i];
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: {
width: photoWidth,
height: rowHeight,
marginRight: i === row.length - 1 ? 0 : gap
},
children: renderJustifiedPhotoItem(p, photoWidth, rowHeight, i === row.length - 1)
}, p.id);
})
}, rowIndex);
})
})]
}, date);
})
})
});
};
var _default = exports.default = /*#__PURE__*/_react.default.memo(JustifiedPhotoGrid);
//# sourceMappingURL=JustifiedPhotoGrid.js.map