@fto-consult/common
Version:
Un ensemble de bibliothèques et d'utilistaires communs pour le développement d'applications javascript
212 lines (201 loc) • 8.67 kB
JavaScript
// Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import Dimensions, {useWindowDimensions} from '$cdimensions';
import breakpoints from "$cplatform/dimensions/breakpoints";
import {isNumber,parseDecimal,defaultStr,isFunction,isNonNullString,defaultVal,isObj,defaultObj} from "$cutils";
import {canBeNumber} from "./utils";
let colWidth = 100 / 12;
export const medias = {
sp:3,//maxWidth = 320
mp:4, //maxWidth = 399
xs :5,//575, // Small devices (landscape phones, 576px and up)
sm:6,//767,// Medium devices (tablets, 768px and up)
md:7,//1024,,
lg:8,//1199, // Extra large devices (large desktops, 1200px and up)
}
export const mediaQueries = "col-4 phone-12 tablet-6 desktop-4";
function rowCol(width,update){
const winWidth = Dimensions.get("window").width;
const hasWidth = isNumber(width) && Math.abs(width-winWidth)>10 && width > MIN_WIDTH?true : false;
width = hasWidth ? width : winWidth;
if(update === true){
breakpoints.update();
}
let cMedia = breakpoints.current && breakpoints.current.name ? breakpoints.current.name : undefined;
let currentMedia = "lg";
if(cMedia =="xl") cMedia = "lg";
if(!hasWidth && cMedia && medias[cMedia]){
currentMedia = cMedia;
} else {
currentMedia = (hasWidth || !cMedia ? breakpoints.getCurrentMedia(width) : cMedia) || "lg";
}
let gutter = medias[currentMedia];
if(!isNumber(gutter)){
gutter = 0;
}
return {
row : {flexDirection:'row', flexWrap:'wrap',marginRight:-1*gutter,gutter,currentMedia},
col : {paddingRight:gutter*1.8,gutter,currentMedia}
}
}
const MIN_WIDTH = 300;
const getWidth = (width)=>{
const _width = Dimensions.get("window").width;
if(typeof width =='number' && width > MIN_WIDTH && width < (_width - 100)){
return width;
}
return _width;
}
/*** retourne les propriétés css du wrapper des colusage
* @param bindEvents {boolean||number}, si l'écoute de l'évènement de redimensionnement de la page sera prise en compte dans ce cas, à chaque fois que la page sera redimenssionnée, la grille sera re-rendue
* si bindEvents est un nombre supérieur à MIN_WIDTH alors il s'agit de la dimensions de la fenêtre à prendre en compte pour le calcul de la props responsive
* @param opts {object}, les options supplémentaires à appliquer à la grille si bindEvents est un objet alors opts est remplacée par bindEvents
* les opts sont de la forme : {marging|margin:bool, spécifie si la marge sera prise en compte}
*/
export function row (bindEvents,opts){
let _width = Dimensions.get('window').width;
if(typeof bindEvents =='boolean'){
opts = {bindEvents};
} else if(typeof bindEvents ==='number' && bindEvents > MIN_WIDTH){
opts = {width:MIN_WIDTH};
} else if(isObj(bindEvents)){
opts = isObj(opts)? opts : bindEvents;
}
opts = defaultObj(opts);
_width = getWidth(opts.width);
bindEvents = typeof opts.bindEvents ==='boolean'? opts.bindEvents : typeof opts.bindResizeEvents ==='boolean'? opts.bindResizeEvents : false;
const marging = typeof opts.noMargin =='boolean' ? !opts.noMargin : defaultBool(opts.margin,true);
if(bindEvents){
_width = useWindowDimensions().width;
}
let {gutter,currentMedia,marginRight,...rest} = rowCol(_width).row;
if(marging){
rest.marginRight = marginRight;
}
return rest;
}
/**** détermnine les styles de la colonne appropriée en fonction des dimensions courante de l'écran
* exemple col("md-5 xs-3 lg-8 sm-10")
* exemple col("md_5 xs_3 lg_3 sm_3")
* exemple col("col-3 xs-4 lg-5") retourne par défaut 3 colonnes par défaut, 4 pour les xs, 5 pour les lg
* les options par défaut de la formule c'est la grille sur 3 colonnes en environnement desktop, une colonne en environnement mobile, 2 colonnes en environnement tablet
* @param : les options responsive de colonne à retrouver
* @param : all {boolean} si toutes les options calculées par la fonction rowCol seront retournées
*/
export function col(opts,all){
let width = undefined;
if(typeof opts =='number' && opts > MIN_WIDTH){
width = opts;
opts = undefined;
}
if(!opts || (typeof opts !=='string' && typeof opts !=='object')){
opts = mediaQueries;
}
width = width ? width : isObj(opts) && isNumber(opts.width) && opts.width > MIN_WIDTH ? opts.width : undefined;
width = getWidth(width);
let {gutter,currentMedia,...rest}= rowCol(width,isObj(opts) ? defaultBool(opts.update,opts.forceUpdate,false) : false).col;
let otherStyle = {}, commonMultiplicater = undefined;
let mQuery = typeof opts === 'string' && opts ? opts :undefined;
if(!mQuery && isObj(opts)){
if((opts.useDefaultMedia || opts.useDefault)){
mQuery = mediaQueries;
} else {
let hasF = typeof opts.col =='number' && opts.col || typeof opts.column =='number' && opts.column ? true : false;
if(!hasF){
for(let i in medias){
if(typeof opts[i] == 'number' && opts[i]){
hasF = true;
break;
}
}
}
if(!hasF) mQuery = mediaQueries;
}
}
if(mQuery){
let split = mQuery.trim().split(" ");
opts = isObj(opts)? opts : {};
for(let i in split){
if(split[i]){
let sp = defaultStr(split[i]).replaceAll("_","-").trim().toLowerCase();
if(sp){
let spSplit = sp.split("-");
let media = spSplit[0],mediaValue = spSplit[1];
if(media =="small-phone" || media =="s-phone"){
media = "sp"
} else if(media =="medium-phone" || media =="m-phone") {
media = "mp";
} else if(media ==='phone'){
media = currentMedia =='xs'? 'xs' : currentMedia == 'sp'? 'sp' : 'mp';
} else if(media =="tablet") {
media = currentMedia == "sm"? "sm" : "md";
} else if(media =='xl' || media =="desktop"){
media = "lg";
}
if(currentMedia === media && spSplit.length ===2){
if(mediaValue ==="hidden"){
otherStyle.display = "none";
} else if(canBeNumber(mediaValue)){
opts = {[currentMedia]:parseDecimal(mediaValue)}
}
} else if((media =="col" || media =="column") && canBeNumber(mediaValue)){
let v = parseDecimal(mediaValue);
if(v <= 12){
commonMultiplicater = v;
}
}
}
}
}
}
let multiplicater = 12;
let hasFound = false;
if(isObj(opts)){
for(let i in opts){
if(i == "col" || i == "column"){
if(isNumber(opts[i]) && opts[i] <=12){
commonMultiplicater = opts[i];
}
} else if(i == currentMedia){
if(isNumber(opts[i]) && opts[i] <=12){
multiplicater = opts[i];
hasFound = true;
break;
}
}
}
}
if(!hasFound && isNumber(commonMultiplicater)){
multiplicater = commonMultiplicater;
}
let ret = {
...rest,
...otherStyle,
width: (colWidth*multiplicater).toFixed(8)+'%'
}
if(all === true){
ret.media = currentMedia;
ret.multiplicater = multiplicater;
ret.opts = opts;
ret.currentMedia = currentMedia;
}
return ret;
}
/**** retourne le nombre de colonnes de la vue actuellement disponible */
export function cols(opts){
const {multiplicater} = col(opts,true);
let cols = 1;
if(multiplicater && multiplicater > 0){
cols = Math.trunc(12/multiplicater);
}
return Math.max(cols,1);
}
export const columns = cols;
export default {
row,
numColumns : cols,
cols,
columns,
col
}