Drag the border between two columns to set their widths in 1.16.0
Umut, with a card of an icon next to a text as the example: "bei Rows die Breite ändern, am besten per Drag and Drop im Editor". Rows only ever had equal columns - both the classic `columns` token and the free `gridColumns` produce `repeat(n, 1fr)`. A row now carries the free style `columnWidths`, a list of shares such as `2 1` or `66.7 33.3`, per device and without a new document schema. `StyleCompiler::columnTracks()` turns it into `grid-template-columns`, pads or cuts the shares to the number of columns, and gives a device whose layout stacks the columns a single track, because the desktop rule carries an id and would otherwise outrank the class that stacks a phone. In the editor the border between two side-by-side columns of the selected row is a handle: dragging restyles the row inline while the pointer moves and writes one undo step on release; the arrow keys nudge by five points. The design panel shows the same value as the text field "Spaltenbreiten". The Playwright editor check drags the handle with the real mouse. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
f6350de636
commit
32ee28888e
14 files changed
+346
-3
No files matched your search
@@ -92,6 +92,11 @@ final class StyleCompiler
|
||||
sprintf('"%s" is outside its range.', $key),
|
||||
),
|
||||
'font' => $this->require(1 === preg_match('/^[a-z][a-z0-9-]{0,39}$/D', $text), sprintf('"%s" is not a font key.', $key)),
|
||||
// A share of nothing is a column that cannot be seen, so zero is refused with the rest.
|
||||
'fractions' => $this->require(
|
||||
1 === preg_match(StyleSchema::FRACTIONS, $text) && !in_array(0.0, array_map(floatval(...), explode(' ', $text)), true),
|
||||
sprintf('"%s" is not a list of column shares.', $key),
|
||||
),
|
||||
'media' => $this->require('none' === $text || 1 === preg_match('/^[a-f0-9]{32}$/D', $text), sprintf('"%s" is not a media ID.', $key)),
|
||||
default => $this->require(false, 'Unknown style type.'),
|
||||
};
|
||||
@@ -112,6 +117,9 @@ final class StyleCompiler
|
||||
if ('image' === ($block['type'] ?? null)) {
|
||||
$css .= $this->imageAlignment((string) $block['id'], $block['style'], $parent);
|
||||
}
|
||||
if ('row' === ($block['type'] ?? null)) {
|
||||
$css .= $this->columnTracks((string) $block['id'], $block['style'], is_array($block['layout'] ?? null) ? $block['layout'] : [], count($block['children'] ?? []));
|
||||
}
|
||||
}
|
||||
$walk($block['children'] ?? [], $block);
|
||||
}
|
||||
@@ -461,6 +469,55 @@ final class StyleCompiler
|
||||
return $chosen;
|
||||
}
|
||||
|
||||
/**
|
||||
* The column shares of a row: "2 1" becomes two tracks of 2fr and 1fr. Shares inherit from the
|
||||
* wider device like every other style. A device whose layout stacks the columns - "one" from
|
||||
* the classic tokens or a single grid column - gets one track instead: the desktop rule carries
|
||||
* an id and would otherwise outrank the class that stacks the columns on a phone.
|
||||
*/
|
||||
private function columnTracks(string $id, array $style, array $layout, int $count): string
|
||||
{
|
||||
$css = $previous = '';
|
||||
$widths = null;
|
||||
$stacked = false;
|
||||
foreach (['desktop', 'tablet', 'mobile'] as $device) {
|
||||
$values = is_array($style[$device] ?? null) ? $style[$device] : [];
|
||||
$widths = $values['columnWidths'] ?? $widths;
|
||||
if (isset($values['gridColumns'])) {
|
||||
$stacked = 1 === (int) $values['gridColumns'];
|
||||
} elseif (isset($layout['design'][$device]['columns'])) {
|
||||
$stacked = 'one' === $layout['design'][$device]['columns'];
|
||||
}
|
||||
if (null === $widths || $count < 2) {
|
||||
continue;
|
||||
}
|
||||
$rule = sprintf('#%s{grid-template-columns:%s}', $id, $stacked ? 'minmax(0,1fr)' : $this->tracks((string) $widths, $count));
|
||||
if ($rule === $previous) {
|
||||
continue;
|
||||
}
|
||||
$css .= isset(self::BREAKPOINTS[$device])
|
||||
? sprintf('@media (max-width:%dpx){%s}', self::BREAKPOINTS[$device], $rule)
|
||||
: $rule;
|
||||
$previous = $rule;
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/** One track per column: a column added later gets the average share, surplus shares are dropped. */
|
||||
private function tracks(string $widths, int $count): string
|
||||
{
|
||||
$shares = array_values(array_filter(array_map(floatval(...), explode(' ', $widths)), static fn (float $share): bool => $share > 0));
|
||||
$shares = array_slice($shares, 0, $count);
|
||||
$average = [] === $shares ? 1.0 : array_sum($shares) / count($shares);
|
||||
while (count($shares) < $count) {
|
||||
$shares[] = $average;
|
||||
}
|
||||
$format = static fn (float $share): string => rtrim(rtrim(number_format($share, 1, '.', ''), '0'), '.');
|
||||
|
||||
return implode(' ', array_map(static fn (float $share): string => sprintf('minmax(0,%sfr)', $format($share)), $shares));
|
||||
}
|
||||
|
||||
private function grid(array $values): string
|
||||
{
|
||||
return isset($values['gridColumns'])
|
||||
|
||||
Reference in new issue
Block a user