Skip to content

Commit

Permalink
feat: add grid component
Browse files Browse the repository at this point in the history
  • Loading branch information
grantjbutler committed Apr 3, 2022
1 parent 6826fac commit 0130515
Show file tree
Hide file tree
Showing 6 changed files with 413 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<Controls title="Grid Component">
<FormNumberInput
v-model="columnCount"
label="Column Count"
:shows-slider="isWindows"
/>
<FormNumberInput
v-model="columnSpacing"
label="Column Spacing"
:shows-slider="isWindows"
/>
<FormNumberInput
v-model="rowSpacing"
label="Row Spacing"
:shows-slider="isWindows"
/>
<FormSelect
v-model="distribution"
label="Distribution"
>
<option value="leading">
Leading
</option>
<option value="center">
Center
</option>
<option value="trailing">
Trailing
</option>
<option value="justify-between">
Justify Between
</option>
</FormSelect>
</Controls>
</template>

<script lang="ts" setup>
import type { GridComponent } from '/@/layout';
import { useIsWindows } from '/@/integration/platform';
import { computed } from 'vue';
import FormSelect from '/@/components/Form/FormSelect.vue';
import FormNumberInput from '/@/components/Form/FormNumberInput.vue';
import Controls from './Controls.vue';
import { setColumnCount, setColumnSpacing, setDistribution, setRowSpacing } from '/@/store/components/grid';
const props = defineProps<{
component: GridComponent
}>();
const isWindows = useIsWindows();
const distribution = computed({
get(): 'leading' | 'center' | 'trailing' | 'justify-between' { return props.component.distribution; },
set(distribution: 'leading' | 'center' | 'trailing' | 'justify-between') { setDistribution(distribution); },
});
const columnSpacing = computed({
get() { return props.component.columnSpacing; },
set(spacing: number) { setColumnSpacing(spacing); },
});
const rowSpacing = computed({
get() { return props.component.rowSpacing; },
set(spacing: number) { setRowSpacing(spacing); },
});
const columnCount = computed({
get() { return props.component.columnCount; },
set(count: number) { setColumnCount(count); },
});
</script>
5 changes: 4 additions & 1 deletion packages/renderer/src/components/Controls/Registry.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import FlexComponentControls from './FlexComponentControls.vue';
import GridComponentControls from './GridComponentControls.vue';
import InsetComponentControls from './InsetComponentControls.vue';
import SourceComponentControls from './SourceComponentControls.vue';

import { FlexComponent, InsetComponent, SourceComponent } from '/@/layout';
import { FlexComponent, GridComponent, InsetComponent, SourceComponent } from '/@/layout';

export default {
components: {
FlexComponentControls,
GridComponentControls,
InsetComponentControls,
SourceComponentControls,
},
registry: new Map<any, any>([
[FlexComponent, FlexComponentControls],
[GridComponent, GridComponentControls],
[InsetComponent, InsetComponentControls],
[SourceComponent, SourceComponentControls],
]),
Expand Down
98 changes: 98 additions & 0 deletions packages/renderer/src/layout/GridComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import ContainerLayoutNode from './ContainerLayoutNode';
import ContainerComponent from './ContainerComponent';
import Frame from './Frame';
import Size from './Size';
import type LayoutNode from './LayoutNode';
import type Component from './Component';

export default class GridComponent extends ContainerComponent {
rowSpacing = 0;
columnSpacing = 0;
columnCount = 1;
distribution: 'leading' | 'center' | 'trailing' | 'justify-between' = 'center';

static get displayName(): string {
return 'Grid Component';
}

exerciseLayout(size: Size): LayoutNode {
if (!this.children.length) {
return new ContainerLayoutNode(this.id, new Frame(0, 0, size.width, size.height));
}

const rowCount = Math.ceil(this.children.length / this.columnCount);
const totalColumnSpacing = this.columnSpacing * (this.columnCount - 1);
const totalRowSpacing = (rowCount - 1) * this.rowSpacing;

if (totalColumnSpacing >= size.width || totalRowSpacing >= size.height) {
return new ContainerLayoutNode(this.id, new Frame(0, 0, size.width, size.height));
}

let availableHeight = size.height;
const childSize = new Size((size.width - totalColumnSpacing) / this.columnCount, (size.height - totalRowSpacing) / rowCount);
let childNodes: LayoutNode[] = [];
let yOffset = 0;
let containerHeight = (rowCount - 1) * this.rowSpacing;

for (let row = 0; row < rowCount; row++) {
const rowNodes: LayoutNode[] = [];

for (let column = 0; column < this.columnCount; column++) {
const index = column + (row * this.columnCount);
if (index >= this.children.length) { break; }

rowNodes.push(this.children[index].exerciseLayout(childSize));
}

const rowHeight = rowNodes.reduce((height, node) => Math.max(height, node.frame.height), 0);

rowNodes.forEach((node, column) => {
let xOffset = (column % this.columnCount) * (childSize.width + this.columnSpacing);

if (row == rowCount - 1 && rowNodes.length != this.columnCount) {
switch (this.distribution) {
case 'center': {
const totalChildWidth = rowNodes.length * childSize.width + (rowNodes.length - 1) * this.columnSpacing;
xOffset += (size.width - totalChildWidth) / 2.0;
break;
}
case 'leading':
break;
case 'trailing':
xOffset += (this.columnCount - rowNodes.length) * (childSize.width + this.columnSpacing);
break;
case 'justify-between': {
const spacing = (size.width - (rowNodes.length * childSize.width)) / (rowNodes.length - 1);
xOffset = (column % rowNodes.length) * (childSize.width + spacing);
break;
}
}
}

node.frame.x = xOffset + (childSize.width - node.frame.width) / 2.0;
node.frame.y = yOffset + (rowHeight - node.frame.height) / 2.0;
});

availableHeight -= rowHeight + this.rowSpacing;
yOffset += rowHeight + this.rowSpacing;
containerHeight += rowHeight;

const remainingRowCount = rowCount - (row + 1);
childSize.height = (availableHeight - (remainingRowCount - 1) * this.rowSpacing) / remainingRowCount;

childNodes = childNodes.concat(rowNodes);
}

return new ContainerLayoutNode(this.id, new Frame(0, 0, size.width, containerHeight), childNodes);
}

clone(): Component {
const clone = new GridComponent();
clone.columnSpacing = this.columnSpacing;
clone.columnCount = this.columnCount;
clone.rowSpacing = this.rowSpacing;
clone.distribution = this.distribution;
this.children.forEach(child => clone.addChild(child.clone()));
return clone;
}
}
4 changes: 4 additions & 0 deletions packages/renderer/src/layout/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Component from './Component';
import FlexComponent from './FlexComponent';
import GridComponent from './GridComponent';
import InsetComponent from './InsetComponent';
import SourceComponent from './SourceComponent';
import LayoutExerciser from './LayoutExerciser';
Expand All @@ -13,12 +14,14 @@ import Insets from './Insets';

export const components: { [index: string]: typeof Component } = {
FlexComponent,
GridComponent,
InsetComponent,
SourceComponent,
};

export const containerComponents: { [index: string]: typeof ContainerComponent } = {
FlexComponent,
GridComponent,
InsetComponent,
};

Expand All @@ -32,6 +35,7 @@ export {
Component,
ContainerComponent,
FlexComponent,
GridComponent,
InsetComponent,
SourceComponent,
LayoutExerciser,
Expand Down
42 changes: 42 additions & 0 deletions packages/renderer/src/store/components/grid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useLayoutStore } from '/@/store/layout';
import { GridComponent } from '/@/layout';

export const setDistribution = (distribution: 'leading' | 'center' | 'trailing' | 'justify-between') => {
const store = useLayoutStore();
if (!store.selectedComponent || !(store.selectedComponent instanceof GridComponent)) {
return;
}
store.selectedComponent.distribution = distribution;

store.exerciseLayout();
};

export const setColumnSpacing = (spacing: number) => {
const store = useLayoutStore();
if (!store.selectedComponent || !(store.selectedComponent instanceof GridComponent)) {
return;
}
store.selectedComponent.columnSpacing = spacing;

store.exerciseLayout();
};

export const setRowSpacing = (spacing: number) => {
const store = useLayoutStore();
if (!store.selectedComponent || !(store.selectedComponent instanceof GridComponent)) {
return;
}
store.selectedComponent.rowSpacing = spacing;

store.exerciseLayout();
};

export const setColumnCount = (count: number) => {
const store = useLayoutStore();
if (!store.selectedComponent || !(store.selectedComponent instanceof GridComponent)) {
return;
}
store.selectedComponent.columnCount = count;

store.exerciseLayout();
};
Loading

0 comments on commit 0130515

Please sign in to comment.