mirror of https://github.com/Budibase/budibase.git
Browse Source
* Implement collapsing component hierarchy. * Save screen when adding new components. * Allow creation of nested child components. * Rename updateComponentProps to setComponentProps * Compile layout and position properties to css strings. * Correct ordering errors. * Compile the css for an entire screen. * Add unique id for each component. * Ignore _id props. * Update client to add correct class names to component elements. * Add grid-template fields to layout styling panel. * Inject css into iframe. Minor tweaks. * Fix unset margins. * Update failing tests.pull/4023/head
committed by
GitHub
25 changed files with 134034 additions and 123856 deletions
@ -0,0 +1,118 @@ |
|||
import { filter, map, reduce, toPairs } from "lodash/fp"; |
|||
import { pipe } from "../common/core"; |
|||
|
|||
const self = n => n; |
|||
const join_with = delimiter => a => a.join(delimiter); |
|||
const empty_string_to_unset = s => s.length ? s : "0"; |
|||
const add_suffix = suffix => s => s + suffix; |
|||
|
|||
export const make_margin = (values) => pipe(values, [ |
|||
map(empty_string_to_unset), |
|||
map(add_suffix('px')), |
|||
join_with(' ') |
|||
]); |
|||
|
|||
const tap = message => x => { |
|||
console.log(x); |
|||
return x; |
|||
} |
|||
|
|||
const css_map = { |
|||
templaterows: { |
|||
name: 'grid-template-columns', |
|||
generate: self |
|||
}, |
|||
templatecolumns: { |
|||
name: 'grid-template-rows', |
|||
generate: self |
|||
}, |
|||
gridarea: { |
|||
name: 'grid-area', |
|||
generate: make_margin |
|||
}, |
|||
gap: { |
|||
name: 'grid-gap', |
|||
generate: n => `${n}px` |
|||
}, |
|||
columnstart: { |
|||
name: 'grid-column-start', |
|||
generate: self |
|||
}, |
|||
columnend: { |
|||
name: 'grid-column-end', |
|||
generate: self |
|||
}, |
|||
rowstart: { |
|||
name: 'grid-row-start', |
|||
generate: self |
|||
}, |
|||
rowend: { |
|||
name: 'grid-row-end', |
|||
generate: self |
|||
}, |
|||
padding: { |
|||
name: 'padding', |
|||
generate: make_margin |
|||
}, |
|||
margin: { |
|||
name: 'margin', |
|||
generate: make_margin |
|||
}, |
|||
zindex: { |
|||
name: 'z-index', |
|||
generate: self |
|||
} |
|||
} |
|||
|
|||
export const generate_rule = ([name, values]) => |
|||
`${css_map[name].name}: ${css_map[name].generate(values)};` |
|||
|
|||
const handle_grid = (acc, [name, value]) => { |
|||
let tmp = []; |
|||
|
|||
if (name === 'row' || name === 'column') { |
|||
if (value[0]) tmp.push([`${name}start`, value[0]]); |
|||
if (value[1]) tmp.push([`${name}end`, value[1]]); |
|||
return acc.concat(tmp) |
|||
} |
|||
|
|||
return acc.concat([[name, value]]); |
|||
} |
|||
|
|||
const object_to_css_string = [ |
|||
toPairs, |
|||
reduce(handle_grid, []), |
|||
filter(v => Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length), |
|||
map(generate_rule), |
|||
join_with('\n'), |
|||
]; |
|||
|
|||
export const generate_css = ({ layout, position }) => { |
|||
let _layout = pipe(layout, object_to_css_string); |
|||
_layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout; |
|||
|
|||
return { |
|||
layout: _layout, |
|||
position: pipe(position, object_to_css_string) |
|||
} |
|||
} |
|||
|
|||
const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}`; |
|||
|
|||
export const generate_screen_css = (component_array) => { |
|||
let styles = ""; |
|||
|
|||
for (let i = 0; i < component_array.length; i += 1) { |
|||
const { _styles, _id, _children } = component_array[i]; |
|||
const { layout, position } = generate_css(_styles); |
|||
|
|||
styles += apply_class(_id, 'pos', position) + "\n"; |
|||
styles += apply_class(_id, 'lay', layout) + "\n"; |
|||
|
|||
if (_children && _children.length) { |
|||
styles += generate_screen_css(_children) + "\n"; |
|||
} |
|||
|
|||
} |
|||
return styles.trim(); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
export function uuid() { |
|||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { |
|||
const r = (Math.random() * 16) | 0, |
|||
v = c == 'x' ? r : (r & 0x3) | 0x8; |
|||
return v.toString(16); |
|||
}); |
|||
} |
|||
@ -0,0 +1,337 @@ |
|||
import { generate_css, make_margin, generate_screen_css } from '../src/builderStore/generate_css.js'; |
|||
|
|||
describe('make_margin', () => { |
|||
test('it should generate a valid rule', () => { |
|||
expect(make_margin(["1", "1", "1", "1"])).toEqual('1px 1px 1px 1px') |
|||
}) |
|||
|
|||
test('empty values should output 0', () => { |
|||
expect(make_margin(["1", "1", "", ""])).toEqual('1px 1px 0px 0px') |
|||
expect(make_margin(["1", "", "", "1"])).toEqual('1px 0px 0px 1px') |
|||
expect(make_margin(["", "", "", ""])).toEqual('0px 0px 0px 0px') |
|||
}) |
|||
}) |
|||
|
|||
describe('generate_css', () => { |
|||
test('it should generate a valid css rule: grid-area', () => { |
|||
expect( |
|||
generate_css({ layout: { gridarea: ["", "", "", ""] } }) |
|||
).toEqual({ |
|||
layout: '', |
|||
position: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: grid-gap', () => { |
|||
expect( |
|||
generate_css({ layout: { gap: "10" } }) |
|||
).toEqual({ |
|||
layout: 'grid-gap: 10px;\ndisplay: grid;', |
|||
position: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: column 1', () => { |
|||
expect( |
|||
generate_css({ position: { column: ["", ""] } } |
|||
)).toEqual({ layout: '', position: '' }); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: column 2', () => { |
|||
expect( |
|||
generate_css({ position: { column: ["1", ""] } }) |
|||
).toEqual({ |
|||
position: 'grid-column-start: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: column 3', () => { |
|||
expect( |
|||
generate_css({ position: { column: ["", "1"] } }) |
|||
).toEqual({ |
|||
position: 'grid-column-end: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: column 4', () => { |
|||
expect( |
|||
generate_css({ position: { column: ["1", "1"] } }) |
|||
).toEqual({ |
|||
position: 'grid-column-start: 1;\ngrid-column-end: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: row 1', () => { |
|||
expect( |
|||
generate_css({ position: { row: ["", ""] } }) |
|||
).toEqual({ layout: '', position: '' }); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: row 2', () => { |
|||
expect( |
|||
generate_css({ position: { row: ["1", ""] } }) |
|||
).toEqual({ |
|||
position: 'grid-row-start: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: row 3', () => { |
|||
expect( |
|||
generate_css({ position: { row: ["", "1"] } }) |
|||
).toEqual({ |
|||
position: 'grid-row-end: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: row 4', () => { |
|||
expect( |
|||
generate_css({ position: { row: ["1", "1"] } }) |
|||
).toEqual({ |
|||
position: 'grid-row-start: 1;\ngrid-row-end: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: padding 1', () => { |
|||
expect( |
|||
generate_css({ position: { padding: ["1", "1", "1", "1"] } }) |
|||
).toEqual({ |
|||
position: 'padding: 1px 1px 1px 1px;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: padding 2', () => { |
|||
expect( |
|||
generate_css({ position: { padding: ["1", "", "", "1"] } }) |
|||
).toEqual({ |
|||
position: 'padding: 1px 0px 0px 1px;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: margin 1', () => { |
|||
expect( |
|||
generate_css({ position: { margin: ["1", "1", "1", "1"] } }) |
|||
).toEqual({ |
|||
position: 'margin: 1px 1px 1px 1px;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: margin 2', () => { |
|||
expect( |
|||
generate_css({ position: { margin: ["1", "", "", "1"] } }) |
|||
).toEqual({ |
|||
position: 'margin: 1px 0px 0px 1px;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: z-index 1', () => { |
|||
expect( |
|||
generate_css({ position: { zindex: "" } }) |
|||
).toEqual({ |
|||
position: '', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
test('it should generate a valid css rule: z-index 2', () => { |
|||
expect( |
|||
generate_css({ position: { zindex: "1" } }) |
|||
).toEqual({ |
|||
position: 'z-index: 1;', |
|||
layout: '' |
|||
}); |
|||
}) |
|||
|
|||
}) |
|||
|
|||
describe('generate_screen_css', () => { |
|||
test('it should compile the css for a list of components', () => { |
|||
const components = [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 1 |
|||
}, |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 2 |
|||
}, { |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 3 |
|||
} |
|||
, { |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 4 |
|||
} |
|||
] |
|||
|
|||
const compiled = `.pos-1 {
|
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-1 { |
|||
|
|||
} |
|||
.pos-2 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-2 { |
|||
|
|||
} |
|||
.pos-3 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-3 { |
|||
|
|||
} |
|||
.pos-4 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-4 { |
|||
|
|||
}` |
|||
|
|||
expect(generate_screen_css(components)).toEqual(compiled) |
|||
}) |
|||
|
|||
test('it should compile the css for a list of components', () => { |
|||
const components = [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 1, |
|||
_children: [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 2, |
|||
_children: [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 3, |
|||
_children: [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 4, |
|||
_children: [ |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 5, |
|||
_children: [ |
|||
|
|||
] |
|||
}, |
|||
] |
|||
}, |
|||
] |
|||
}, |
|||
] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 6 |
|||
}, { |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 7 |
|||
} |
|||
, { |
|||
_styles: { |
|||
layout: { gridarea: ["", "", "", ""] }, |
|||
position: { margin: ["1", "1", "1", "1"] } |
|||
}, |
|||
_id: 8 |
|||
} |
|||
] |
|||
|
|||
const compiled = `.pos-1 {
|
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-1 { |
|||
|
|||
} |
|||
.pos-2 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-2 { |
|||
|
|||
} |
|||
.pos-3 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-3 { |
|||
|
|||
} |
|||
.pos-4 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-4 { |
|||
|
|||
} |
|||
.pos-5 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-5 { |
|||
|
|||
} |
|||
.pos-6 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-6 { |
|||
|
|||
} |
|||
.pos-7 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-7 { |
|||
|
|||
} |
|||
.pos-8 { |
|||
margin: 1px 1px 1px 1px; |
|||
} |
|||
.lay-8 { |
|||
|
|||
}` |
|||
|
|||
expect(generate_screen_css(components)).toEqual(compiled) |
|||
}) |
|||
}) |
|||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue