400){throw new \InvalidArgumentException('Grid tracks must be a short list of widths.');}$raw=trim($raw);if($raw===''){return '';} $tokens=[];$token='';$depth=0; foreach(str_split($raw) as $c){if($c==='('&&++$depth>1){throw new \InvalidArgumentException('Nested grid expressions are not supported.');}if($c===')'&&--$depth<0){throw new \InvalidArgumentException('Invalid grid expression.');}if(ctype_space($c)&&$depth===0){if($token!==''){$tokens[]=$token;$token='';}}else{$token.=$c;}} if($depth!==0){throw new \InvalidArgumentException('Close the grid expression.');}if($token!==''){$tokens[]=$token;} if(count($tokens)>12){throw new \InvalidArgumentException('Use at most 12 grid tracks.');} foreach($tokens as &$part){$part=preg_replace('/\s+/','',$part);if(in_array($part,['auto','min-content','max-content'],true)){continue;} if(preg_match('/^minmax\(([^,]+),([^,]+)\)$/D',$part,$m)){if(self::length($m[1],false)&&self::length($m[2],true)){continue;}} elseif(self::length($part,true)){continue;} throw new \InvalidArgumentException('Use widths such as 1fr, 280px, 40% or minmax(180px,1fr).'); }return implode(' ',$tokens); } private static function length(string $s,bool $fraction): bool { if($s==='0'){return true;}if(!preg_match('/^(\d+(?:\.\d+)?)(px|fr|%)$/D',$s,$m)){return false;}$v=(float)$m[1];return $v>0&&$v<=($m[2]==='px'?2000:($m[2]==='%'?100:24))&&($fraction||$m[2]!=='fr'); } public static function clean_style(array $s): array { $out=[];foreach(['Desktop','Tablet','Mobile'] as $device){foreach(['ColumnStart'=>12,'ColumnSpan'=>12,'RowStart'=>100,'RowSpan'=>12] as $key=>$max){$name='grid'.$key.$device;$out[$name]=(int)Document::number($s[$name]??($device==='Desktop'?0:-1),$device==='Desktop'?0:-1,$max);}}return $out; } private static function level(array $p,string $name,int $level,$fallback){$value=$p[$name]??$fallback;foreach(array_slice(['Tablet','Mobile'],0,$level) as $device){$v=$p[$name.$device]??null;if($v!==null&&$v!==''&&$v!==-1&&$v!==0){$value=$v;}}return $value;} private static function gap(array $p,string $name,int $level): float {$v=(float)$p['gap'];foreach(array_slice(['','Tablet','Mobile'],0,$level+1) as $suffix){if(($p[$name.$suffix]??-1)>=0){$v=(float)$p[$name.$suffix];}}return $v;} private static function media(string $css,int $level,array $settings): string {return $level?'@media(max-width:'.(int)$settings[$level===1?'tabletBreakpoint':'mobileBreakpoint'].'px){'.$css.'}':$css;} public static function css(array $n,?array $parent,array $settings): string { $css='';$id='#dbs-'.$n['id'];$p=$n['props'];$mode=$p['gridMode']??'legacy'; if($n['type']==='section'&&$mode!=='legacy'){ for($level=0;$level<3;$level++){ $tracks=self::level($p,'gridTracks',$level,'')?:'repeat('.(int)$p['columns'].',minmax(0,1fr))'; if($mode==='auto-fit'){$min=(int)self::level($p,'gridMinWidth',$level,240);$tracks='repeat(auto-fit,minmax(min(100%,'.$min.'px),1fr))';} $rule=$id.'>.dbs-grid{display:grid;grid-template-columns:'.$tracks.';row-gap:'.self::gap($p,'gridRowGap',$level).'px;column-gap:'.self::gap($p,'gridColumnGap',$level).'px;grid-auto-flow:'.(($p['gridDense']??'no')==='yes'?'row dense':'row').';}'; $css.=self::media($rule,$level,$settings); } } if($parent&&$parent['type']==='section'&&($parent['props']['gridMode']??'legacy')!=='legacy'){ $pp=$parent['props'];$values=['ColumnStart'=>0,'ColumnSpan'=>0,'RowStart'=>0,'RowSpan'=>0]; for($level=0;$level<3;$level++){ foreach($values as $key=>&$value){$v=$n['style']['grid'.$key.['Desktop','Tablet','Mobile'][$level]]??-1;if($v>=0){$value=(int)$v;}}unset($value); $start=$values['ColumnStart'];$span=$values['ColumnSpan']?:1; if($pp['gridMode']==='auto-fit'){$column='auto';}else{ $tracks=self::level($pp,'gridTracks',$level,'');$count=$tracks?count(explode(' ',$tracks)):(int)$pp['columns']; $start=min($count,$start);$span=min($span,$count-($start?:1)+1);$column=($start?:'auto').' / span '.$span; } $row=($values['RowStart']?:'auto').' / span '.($values['RowSpan']?:1); $css.=self::media($id.'{grid-column:'.$column.';grid-row:'.$row.';min-width:0;}',$level,$settings); } } return Assets::inline_style($css,'grid-'.substr(hash('sha256',$css),0,16)); } }