forked from tsai/budibase
49 changed files with 140153 additions and 124104 deletions
@ -0,0 +1,23 @@ |
|||||
|
{ |
||||
|
"env": { |
||||
|
"browser": true, |
||||
|
"es6": true, |
||||
|
"jest": true |
||||
|
}, |
||||
|
"parserOptions": { |
||||
|
"ecmaVersion": 2019, |
||||
|
"sourceType": "module" |
||||
|
}, |
||||
|
"ignorePatterns": ["node_modules", "dist", "public"], |
||||
|
"plugins": ["prettier", "svelte3"], |
||||
|
"extends": ["eslint:recommended"], |
||||
|
"overrides": [ |
||||
|
{ |
||||
|
"files": ["**/*.svelte"], |
||||
|
"processor": "svelte3/svelte3" |
||||
|
} |
||||
|
], |
||||
|
"rules": { |
||||
|
"prettier/prettier": "error" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
name: Budibase CI |
||||
|
|
||||
|
on: |
||||
|
# Trigger the workflow on push or pull request, |
||||
|
# but only for the master branch |
||||
|
push: |
||||
|
branches: |
||||
|
- master |
||||
|
pull_request: |
||||
|
branches: |
||||
|
- master |
||||
|
|
||||
|
jobs: |
||||
|
build: |
||||
|
|
||||
|
runs-on: ubuntu-latest |
||||
|
|
||||
|
strategy: |
||||
|
matrix: |
||||
|
node-version: [10.x] |
||||
|
|
||||
|
steps: |
||||
|
- uses: actions/checkout@v2 |
||||
|
- name: Use Node.js ${{ matrix.node-version }} |
||||
|
uses: actions/setup-node@v1 |
||||
|
with: |
||||
|
node-version: ${{ matrix.node-version }} |
||||
|
- run: yarn |
||||
|
- run: yarn lint |
||||
|
- run: yarn build |
||||
|
- run: yarn test |
||||
|
env: |
||||
|
CI: true |
||||
|
name: Budibase CI |
||||
|
|
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"tabWidth": 2, |
||||
|
"semi": false, |
||||
|
"singleQuote": false, |
||||
|
"trailingComma": "es5", |
||||
|
"plugins": ["prettier-plugin-svelte"] |
||||
|
} |
||||
@ -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,53 @@ |
|||||
|
<script> |
||||
|
export let disabled = false; |
||||
|
export let hidden = false; |
||||
|
export let primary = true; |
||||
|
export let alert = false; |
||||
|
export let warning = false; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.primary { |
||||
|
color: #0055ff; |
||||
|
background: rgb(54, 133, 249, 0.1); |
||||
|
} |
||||
|
|
||||
|
.alert { |
||||
|
color: rgba(255, 0, 31, 1); |
||||
|
background: rgba(255, 0, 31, 0.1);; |
||||
|
} |
||||
|
|
||||
|
.button { |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
border-radius: 5px; |
||||
|
border: none; |
||||
|
width: 167px; |
||||
|
height: 64px; |
||||
|
} |
||||
|
|
||||
|
.button:hover { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.button:disabled { |
||||
|
color: rgba(22, 48, 87, 0.2); |
||||
|
cursor: default; |
||||
|
background: transparent; |
||||
|
} |
||||
|
|
||||
|
.hidden { |
||||
|
visibility: hidden; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<button |
||||
|
on:click |
||||
|
class="button" |
||||
|
class:hidden |
||||
|
class:primary |
||||
|
class:alert |
||||
|
class:warning |
||||
|
{disabled}> |
||||
|
<slot /> |
||||
|
</button> |
||||
|
After Width: | Height: | Size: 228 B |
|
After Width: | Height: | Size: 446 B |
@ -0,0 +1,27 @@ |
|||||
|
<script> |
||||
|
export let value = ""; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
input { |
||||
|
display: block; |
||||
|
font-size: 14px; |
||||
|
font-family: sans-serif; |
||||
|
font-weight: 500; |
||||
|
color: #163057; |
||||
|
line-height: 1.3; |
||||
|
padding: 1em 2.6em 0.9em 1.4em; |
||||
|
width: 100%; |
||||
|
max-width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
margin: 0; |
||||
|
-moz-appearance: none; |
||||
|
-webkit-appearance: none; |
||||
|
appearance: none; |
||||
|
background: #fff; |
||||
|
border: 1px solid #ccc; |
||||
|
height: 50px; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<input type="text" on:change bind:value /> |
||||
@ -1,41 +1,44 @@ |
|||||
<script> |
<script> |
||||
import UIkit from "uikit"; |
import UIkit from "uikit"; |
||||
|
|
||||
export let isOpen = false; |
export let isOpen = false; |
||||
export let onClosed = () => {}; |
export let onClosed = () => {}; |
||||
export let id = ""; |
export let id = ""; |
||||
|
|
||||
let ukModal; |
let ukModal; |
||||
let listenerAdded = false; |
let listenerAdded = false; |
||||
|
|
||||
$: { |
$: { |
||||
if(ukModal && !listenerAdded) { |
if (ukModal && !listenerAdded) { |
||||
listenerAdded = true; |
listenerAdded = true; |
||||
ukModal.addEventListener("hidden", onClosed); |
ukModal.addEventListener("hidden", onClosed); |
||||
} |
} |
||||
|
|
||||
if(ukModal) { |
if (ukModal) { |
||||
if(isOpen) { |
if (isOpen) { |
||||
UIkit.modal(ukModal).show(); |
UIkit.modal(ukModal).show(); |
||||
} else { |
} else { |
||||
UIkit.modal(ukModal).hide(); |
UIkit.modal(ukModal).hide(); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div bind:this={ukModal} uk-modal {id}> |
|
||||
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto> |
|
||||
<slot /> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.uk-modal-dialog { |
||||
|
border-radius: 0.3rem; |
||||
|
width: 60%; |
||||
|
height: 80vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
.uk-modal-dialog { |
<div bind:this={ukModal} uk-modal {id}> |
||||
border-radius: .3rem; |
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto> |
||||
} |
{#if onClosed} |
||||
|
<button class="uk-modal-close-default" type="button" uk-close /> |
||||
</style> |
{/if} |
||||
|
<slot /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|||||
@ -0,0 +1,23 @@ |
|||||
|
<style> |
||||
|
button { |
||||
|
cursor: pointer; |
||||
|
outline: none; |
||||
|
border: none; |
||||
|
border-radius: 5px; |
||||
|
background: rgba(249, 249, 249, 1); |
||||
|
|
||||
|
width: 1.8rem; |
||||
|
height: 1.8rem; |
||||
|
padding-bottom: 10px; |
||||
|
|
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
|
||||
|
font-size: 1.2rem; |
||||
|
font-weight: 700; |
||||
|
color: rgba(22, 48, 87, 1); |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<button on:click>+</button> |
||||
@ -0,0 +1,55 @@ |
|||||
|
<script> |
||||
|
import getIcon from "./icon"; |
||||
|
export let value; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.select-container { |
||||
|
padding-bottom: 10px; |
||||
|
font-size: 0.9rem; |
||||
|
color: var(--secondary50); |
||||
|
font-weight: bold; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
select { |
||||
|
display: block; |
||||
|
font-size: 14px; |
||||
|
font-family: sans-serif; |
||||
|
font-weight: 500; |
||||
|
color: #163057; |
||||
|
line-height: 1.3; |
||||
|
padding: 1em 2.6em 0.9em 1.4em; |
||||
|
width: 100%; |
||||
|
max-width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
margin: 0; |
||||
|
-moz-appearance: none; |
||||
|
-webkit-appearance: none; |
||||
|
appearance: none; |
||||
|
background: #fff; |
||||
|
border: 1px solid #ccc; |
||||
|
height: 50px; |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
position: absolute; |
||||
|
right: 10px; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
margin: auto; |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
pointer-events: none; |
||||
|
color: var(--primary100); |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div class="select-container"> |
||||
|
<select on:change {value}> |
||||
|
<slot /> |
||||
|
</select> |
||||
|
<span class="arrow"> |
||||
|
{@html getIcon('chevron-down', '24')} |
||||
|
</span> |
||||
|
</div> |
||||
@ -1,88 +0,0 @@ |
|||||
<script> |
|
||||
import IconButton from "../common/IconButton.svelte"; |
|
||||
import EventSelector from "./EventSelector.svelte"; |
|
||||
import { |
|
||||
filter |
|
||||
} from "lodash/fp"; |
|
||||
import {EVENT_TYPE_MEMBER_NAME} from "../common/eventHandlers"; |
|
||||
|
|
||||
export let parentProps; |
|
||||
export let propDef; |
|
||||
export let onValueChanged; |
|
||||
|
|
||||
$: events = parentProps[propDef.____name]; |
|
||||
|
|
||||
const addHandler = () => { |
|
||||
const newHandler = {parameters:{}}; |
|
||||
newHandler[EVENT_TYPE_MEMBER_NAME] = ""; |
|
||||
events = [...events, newHandler]; |
|
||||
onValueChanged(events); |
|
||||
} |
|
||||
|
|
||||
const onEventHandlerChanged = (oldEvent) => (newEvent) => { |
|
||||
const indexOfOldEvent = events.indexOf(oldEvent); |
|
||||
const newEvents = [...events]; |
|
||||
newEvents.splice( |
|
||||
events.indexOf(oldEvent), |
|
||||
1, |
|
||||
newEvent); |
|
||||
events = newEvents; |
|
||||
onValueChanged(events); |
|
||||
} |
|
||||
|
|
||||
const removeHandler = (index) => () => { |
|
||||
events = filter(e => e !== events[index])(events); |
|
||||
onValueChanged(events); |
|
||||
} |
|
||||
|
|
||||
</script> |
|
||||
|
|
||||
<div class="root"> |
|
||||
<div class="control-container"> |
|
||||
{#each events as ev, index} |
|
||||
|
|
||||
<div class="handler-container"> |
|
||||
<EventSelector onChanged={onEventHandlerChanged(ev)} |
|
||||
onRemoved={removeHandler(index)} |
|
||||
event={ev} /> |
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
<div class="separator"></div> |
|
||||
{/each} |
|
||||
|
|
||||
<div class="addelement-container" |
|
||||
on:click={addHandler}> |
|
||||
<IconButton icon="plus" |
|
||||
size="12"/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
<style> |
|
||||
.addelement-container { |
|
||||
cursor: pointer; |
|
||||
padding: 3px 0px; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.addelement-container:hover { |
|
||||
background-color: var(--primary25); |
|
||||
margin-top: 5px; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
.control-container { |
|
||||
padding-left: 3px; |
|
||||
background: var(--secondary10); |
|
||||
} |
|
||||
|
|
||||
.separator { |
|
||||
width: 60%; |
|
||||
margin: 10px auto; |
|
||||
border-style:solid; |
|
||||
border-width: 1px 0 0 0; |
|
||||
border-color: var(--primary25); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,105 +0,0 @@ |
|||||
<script> |
|
||||
import IconButton from "../common/IconButton.svelte"; |
|
||||
import StateBindingControl from "./StateBindingControl.svelte"; |
|
||||
import { |
|
||||
find, map, keys, reduce, keyBy |
|
||||
} from "lodash/fp"; |
|
||||
import { pipe, userWithFullAccess } from "../common/core"; |
|
||||
import { EVENT_TYPE_MEMBER_NAME, allHandlers } from "../common/eventHandlers"; |
|
||||
import { store } from "../builderStore"; |
|
||||
|
|
||||
export let event; |
|
||||
export let onChanged; |
|
||||
export let onRemoved; |
|
||||
|
|
||||
let eventType; |
|
||||
let parameters = []; |
|
||||
|
|
||||
|
|
||||
$: events = allHandlers( |
|
||||
{hierarchy: $store.hierarchy}, |
|
||||
userWithFullAccess({ |
|
||||
hierarchy: s.hierarchy, |
|
||||
actions: keyBy("name")($store.actions) |
|
||||
}) |
|
||||
); |
|
||||
|
|
||||
$: if(event) { |
|
||||
eventType = event[EVENT_TYPE_MEMBER_NAME]; |
|
||||
parameters = pipe(event.parameters, [ |
|
||||
keys, |
|
||||
map(k => ({name:k, value:event.parameters[k]})) |
|
||||
]); |
|
||||
} else { |
|
||||
eventType = ""; |
|
||||
parameters = []; |
|
||||
} |
|
||||
|
|
||||
const eventChanged = (type, parameters) => { |
|
||||
const paramsAsObject = reduce( |
|
||||
(obj, p) => { |
|
||||
obj[p.name] = p.value; |
|
||||
return obj; |
|
||||
} |
|
||||
, {} |
|
||||
)(parameters) |
|
||||
|
|
||||
const ev = {}; |
|
||||
ev[EVENT_TYPE_MEMBER_NAME]=type; |
|
||||
ev.parameters = paramsAsObject; |
|
||||
|
|
||||
onChanged(ev); |
|
||||
} |
|
||||
|
|
||||
const eventTypeChanged = (ev) => { |
|
||||
const eType = find(e => e.name === ev.target.value)(events); |
|
||||
const emptyParameters = map(p => ({name:p, value:""}))(eType.parameters); |
|
||||
eventChanged(eType.name, emptyParameters); |
|
||||
} |
|
||||
|
|
||||
const onParameterChanged = index => val => { |
|
||||
const newparameters = [...parameters]; |
|
||||
newparameters[index].value = val; |
|
||||
eventChanged(eventType, newparameters); |
|
||||
} |
|
||||
|
|
||||
</script> |
|
||||
|
|
||||
<div class="type-selector-container"> |
|
||||
<select class="type-selector uk-select uk-form-small " value={eventType} on:change={eventTypeChanged}> |
|
||||
<option></option> |
|
||||
{#each events as ev} |
|
||||
<option value={ev.name}>{ev.name}</option> |
|
||||
{/each} |
|
||||
</select> |
|
||||
|
|
||||
<IconButton icon="trash" |
|
||||
size="12" |
|
||||
on:click={onRemoved}/> |
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
{#if parameters} |
|
||||
{#each parameters as p, index} |
|
||||
|
|
||||
<div> |
|
||||
{p.name} |
|
||||
</div> |
|
||||
<StateBindingControl onChanged={onParameterChanged(index)} |
|
||||
value={p.value} /> |
|
||||
|
|
||||
{/each} |
|
||||
{/if} |
|
||||
|
|
||||
<style> |
|
||||
.type-selector-container { |
|
||||
display: flex; |
|
||||
} |
|
||||
|
|
||||
.type-selector { |
|
||||
border-color: var(--primary50); |
|
||||
border-radius: 2px; |
|
||||
width: 50px; |
|
||||
flex: 1 0 auto; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,164 @@ |
|||||
|
<script> |
||||
|
import Modal from "../../common/Modal.svelte"; |
||||
|
import HandlerSelector from "./HandlerSelector.svelte"; |
||||
|
import IconButton from "../../common/IconButton.svelte"; |
||||
|
import ActionButton from "../../common/ActionButton.svelte"; |
||||
|
import PlusButton from "../../common/PlusButton.svelte"; |
||||
|
import Select from "../../common/Select.svelte"; |
||||
|
import Input from "../../common/Input.svelte"; |
||||
|
import getIcon from "../../common/icon"; |
||||
|
|
||||
|
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers"; |
||||
|
|
||||
|
export let event; |
||||
|
export let eventOptions; |
||||
|
export let open; |
||||
|
export let onClose; |
||||
|
export let onPropChanged; |
||||
|
|
||||
|
let eventType = "onClick"; |
||||
|
let draftEventHandler = { parameters: [] }; |
||||
|
|
||||
|
$: eventData = event || { handlers: [] }; |
||||
|
|
||||
|
const closeModal = () => { |
||||
|
onClose(); |
||||
|
draftEventHandler = { parameters: [] }; |
||||
|
eventData = { handlers: [] }; |
||||
|
}; |
||||
|
|
||||
|
const updateEventHandler = (updatedHandler, index) => { |
||||
|
eventData.handlers[index] = updatedHandler; |
||||
|
}; |
||||
|
|
||||
|
const updateDraftEventHandler = updatedHandler => { |
||||
|
draftEventHandler = updatedHandler; |
||||
|
}; |
||||
|
|
||||
|
const deleteEventHandler = index => { |
||||
|
eventData.handlers.splice(index, 1); |
||||
|
eventData = eventData; |
||||
|
}; |
||||
|
|
||||
|
const createNewEventHandler = handler => { |
||||
|
const newHandler = handler || { |
||||
|
parameters: {}, |
||||
|
[EVENT_TYPE_MEMBER_NAME]: "" |
||||
|
}; |
||||
|
eventData.handlers.push(newHandler); |
||||
|
eventData = eventData; |
||||
|
}; |
||||
|
|
||||
|
const deleteEvent = () => { |
||||
|
onPropChanged(eventType, []); |
||||
|
closeModal(); |
||||
|
}; |
||||
|
|
||||
|
const saveEventData = () => { |
||||
|
onPropChanged(eventType, eventData.handlers); |
||||
|
closeModal(); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
h2 { |
||||
|
color: var(--primary100); |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
h5 { |
||||
|
color: rgba(22, 48, 87, 0.6); |
||||
|
font-size: 15px; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.event-options { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
grid-gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.actions, |
||||
|
header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.actions { |
||||
|
margin-top: auto; |
||||
|
} |
||||
|
|
||||
|
header { |
||||
|
margin-top: 30px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
color: rgba(22, 48, 87, 0.6); |
||||
|
font-size: 12px; |
||||
|
margin-top: 0; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<Modal bind:isOpen={open} onClosed={closeModal}> |
||||
|
<h2> |
||||
|
{eventData.name ? `${eventData.name} Event` : 'Create a New Component Event'} |
||||
|
</h2> |
||||
|
<a href="https://docs.budibase.com/" target="_blank"> |
||||
|
Click here to learn more about component events |
||||
|
</a> |
||||
|
|
||||
|
<div class="event-options"> |
||||
|
<div> |
||||
|
<header> |
||||
|
<h5>Event Type</h5> |
||||
|
{@html getIcon('info', 20)} |
||||
|
</header> |
||||
|
<Select :value={eventType}> |
||||
|
{#each eventOptions as option} |
||||
|
<option value={option.name}>{option.name}</option> |
||||
|
{/each} |
||||
|
</Select> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<header> |
||||
|
<h5>Event Action(s)</h5> |
||||
|
{@html getIcon('info', 20)} |
||||
|
</header> |
||||
|
<HandlerSelector |
||||
|
newHandler |
||||
|
onChanged={updateDraftEventHandler} |
||||
|
onCreate={() => { |
||||
|
createNewEventHandler(draftEventHandler); |
||||
|
draftEventHandler = { parameters: [] }; |
||||
|
}} |
||||
|
handler={draftEventHandler} /> |
||||
|
{#if eventData} |
||||
|
{#each eventData.handlers as handler, index} |
||||
|
<HandlerSelector |
||||
|
{index} |
||||
|
onChanged={updateEventHandler} |
||||
|
onRemoved={() => deleteEventHandler(index)} |
||||
|
{handler} /> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
|
||||
|
<div class="actions"> |
||||
|
<ActionButton |
||||
|
alert |
||||
|
disabled={eventData.handlers.length === 0} |
||||
|
hidden={!eventData.name} |
||||
|
on:click={deleteEvent}> |
||||
|
Delete |
||||
|
</ActionButton> |
||||
|
<ActionButton |
||||
|
disabled={eventData.handlers.length === 0} |
||||
|
on:click={saveEventData}> |
||||
|
Save |
||||
|
</ActionButton> |
||||
|
</div> |
||||
|
</Modal> |
||||
@ -0,0 +1,152 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
keys, |
||||
|
map, |
||||
|
some, |
||||
|
includes, |
||||
|
cloneDeep, |
||||
|
isEqual, |
||||
|
sortBy, |
||||
|
filter, |
||||
|
difference |
||||
|
} from "lodash/fp"; |
||||
|
import { pipe } from "../../common/core"; |
||||
|
import Checkbox from "../../common/Checkbox.svelte"; |
||||
|
import Textbox from "../../common/Textbox.svelte"; |
||||
|
import Dropdown from "../../common/Dropdown.svelte"; |
||||
|
import PlusButton from "../../common/PlusButton.svelte"; |
||||
|
import IconButton from "../../common/IconButton.svelte"; |
||||
|
import Modal from "../../common/Modal.svelte"; |
||||
|
import EventEditorModal from "./EventEditorModal.svelte"; |
||||
|
import HandlerSelector from "./HandlerSelector.svelte"; |
||||
|
|
||||
|
import { PencilIcon } from "../../common/Icons"; |
||||
|
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers"; |
||||
|
|
||||
|
export const EVENT_TYPE = "event"; |
||||
|
|
||||
|
export let componentInfo; |
||||
|
export let onPropChanged = () => {}; |
||||
|
export let components; |
||||
|
|
||||
|
let modalOpen = false; |
||||
|
let events = []; |
||||
|
let selectedEvent = null; |
||||
|
|
||||
|
$: { |
||||
|
events = Object.keys(componentInfo) |
||||
|
.filter(key => findType(key) === EVENT_TYPE) |
||||
|
.map(key => ({ name: key, handlers: componentInfo[key] })); |
||||
|
} |
||||
|
|
||||
|
function findType(propName) { |
||||
|
if (!componentInfo._component) return; |
||||
|
return components.find(({ name }) => name === componentInfo._component) |
||||
|
.props[propName]; |
||||
|
} |
||||
|
|
||||
|
const openModal = event => { |
||||
|
selectedEvent = event; |
||||
|
modalOpen = true; |
||||
|
}; |
||||
|
|
||||
|
const closeModal = () => { |
||||
|
selectedEvent = null; |
||||
|
modalOpen = false; |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
h3 { |
||||
|
text-transform: uppercase; |
||||
|
font-size: 12px; |
||||
|
font-weight: 700; |
||||
|
color: #8997ab; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.root { |
||||
|
font-size: 10pt; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.form-root { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
|
||||
|
header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.handler-container { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(2, 1fr); |
||||
|
border: 2px solid #f9f9f9; |
||||
|
height: 80px; |
||||
|
} |
||||
|
|
||||
|
.hierarchy-item { |
||||
|
cursor: pointer; |
||||
|
padding: 11px 7px; |
||||
|
margin: 5px 0; |
||||
|
border-radius: 5px; |
||||
|
font-size: 1.5em; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.hierarchy-item:hover { |
||||
|
background: #f9f9f9; |
||||
|
} |
||||
|
|
||||
|
.event-name { |
||||
|
margin-top: 5px; |
||||
|
font-weight: bold; |
||||
|
font-size: 16px; |
||||
|
color: rgba(22, 48, 87, 0.6); |
||||
|
align-self: end; |
||||
|
} |
||||
|
|
||||
|
.edit-text { |
||||
|
font-family: Arial, Helvetica, sans-serif; |
||||
|
font-weight: bold; |
||||
|
align-self: end; |
||||
|
justify-self: end; |
||||
|
font-size: 10px; |
||||
|
color: rgba(35, 65, 105, 0.4); |
||||
|
} |
||||
|
|
||||
|
.selected { |
||||
|
color: var(--button-text); |
||||
|
background: var(--background-button) !important; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<header> |
||||
|
<h3>Events</h3> |
||||
|
<PlusButton on:click={() => openModal()} /> |
||||
|
</header> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<form class="uk-form-stacked form-root"> |
||||
|
{#each events as event, index} |
||||
|
{#if event.handlers.length > 0} |
||||
|
<div |
||||
|
class="handler-container hierarchy-item {selectedEvent && selectedEvent.index === index ? 'selected' : ''}" |
||||
|
on:click={() => openModal({ ...event, index })}> |
||||
|
<span class="event-name">{event.name}</span> |
||||
|
<span class="edit-text">EDIT</span> |
||||
|
</div> |
||||
|
{/if} |
||||
|
{/each} |
||||
|
</form> |
||||
|
</div> |
||||
|
<EventEditorModal |
||||
|
{onPropChanged} |
||||
|
open={modalOpen} |
||||
|
onClose={closeModal} |
||||
|
eventOptions={events} |
||||
|
event={selectedEvent} |
||||
|
/> |
||||
@ -0,0 +1,148 @@ |
|||||
|
<script> |
||||
|
import IconButton from "../../common/IconButton.svelte"; |
||||
|
import PlusButton from "../../common/PlusButton.svelte"; |
||||
|
import Select from "../../common/Select.svelte"; |
||||
|
import StateBindingControl from "../StateBindingControl.svelte"; |
||||
|
import { find, map, keys, reduce, keyBy } from "lodash/fp"; |
||||
|
import { pipe, userWithFullAccess } from "../../common/core"; |
||||
|
import { |
||||
|
EVENT_TYPE_MEMBER_NAME, |
||||
|
allHandlers |
||||
|
} from "../../common/eventHandlers"; |
||||
|
import { store } from "../../builderStore"; |
||||
|
|
||||
|
export let handler; |
||||
|
export let onCreate; |
||||
|
export let onChanged; |
||||
|
export let onRemoved; |
||||
|
|
||||
|
export let index; |
||||
|
export let newHandler; |
||||
|
|
||||
|
let eventOptions; |
||||
|
let handlerType; |
||||
|
let parameters = []; |
||||
|
|
||||
|
$: eventOptions = allHandlers( |
||||
|
{ hierarchy: $store.hierarchy }, |
||||
|
userWithFullAccess({ |
||||
|
hierarchy: $store.hierarchy, |
||||
|
actions: keyBy("name")($store.actions) |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
$: { |
||||
|
if (handler) { |
||||
|
handlerType = handler[EVENT_TYPE_MEMBER_NAME]; |
||||
|
parameters = Object.entries(handler.parameters).map(([name, value]) => ({ |
||||
|
name, |
||||
|
value |
||||
|
})); |
||||
|
} else { |
||||
|
// Empty Handler |
||||
|
handlerType = ""; |
||||
|
parameters = []; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handlerChanged = (type, params) => { |
||||
|
const handlerParams = {}; |
||||
|
for (let param of params) { |
||||
|
handlerParams[param.name] = param.value; |
||||
|
} |
||||
|
|
||||
|
const updatedHandler = { |
||||
|
[EVENT_TYPE_MEMBER_NAME]: type, |
||||
|
parameters: handlerParams |
||||
|
}; |
||||
|
|
||||
|
onChanged(updatedHandler, index); |
||||
|
}; |
||||
|
|
||||
|
const handlerTypeChanged = e => { |
||||
|
const handlerType = eventOptions.find( |
||||
|
handler => handler.name === e.target.value |
||||
|
); |
||||
|
const defaultParams = handlerType.parameters.map(param => ({ |
||||
|
name: param, |
||||
|
value: "" |
||||
|
})); |
||||
|
|
||||
|
handlerChanged(handlerType.name, defaultParams); |
||||
|
}; |
||||
|
|
||||
|
const onParameterChanged = index => value => { |
||||
|
const newParams = [...parameters]; |
||||
|
newParams[index].value = value; |
||||
|
handlerChanged(handlerType, newParams); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.type-selector-container { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
background: rgba(223, 223, 223, 0.5); |
||||
|
border: 1px solid #dfdfdf; |
||||
|
margin-bottom: 18px; |
||||
|
} |
||||
|
|
||||
|
.handler-option { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.new-handler { |
||||
|
background: #fff; |
||||
|
} |
||||
|
|
||||
|
.handler-controls { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
grid-gap: 10px; |
||||
|
padding: 22px; |
||||
|
} |
||||
|
|
||||
|
.event-action-button { |
||||
|
margin-right: 20px; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
font-size: 12px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div class="type-selector-container {newHandler && 'new-handler'}"> |
||||
|
<div class="handler-controls"> |
||||
|
<div class="handler-option"> |
||||
|
<span>Action</span> |
||||
|
<Select value={handlerType} on:change={handlerTypeChanged}> |
||||
|
<option /> |
||||
|
{#each eventOptions as option} |
||||
|
<option value={option.name}>{option.name}</option> |
||||
|
{/each} |
||||
|
</Select> |
||||
|
</div> |
||||
|
{#if parameters} |
||||
|
{#each parameters as param, idx} |
||||
|
<div class="handler-option"> |
||||
|
<span>{param.name}</span> |
||||
|
<StateBindingControl |
||||
|
onChanged={onParameterChanged(idx)} |
||||
|
value={param.value} /> |
||||
|
</div> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
</div> |
||||
|
<div class="event-action-button"> |
||||
|
{#if parameters.length > 0} |
||||
|
{#if newHandler} |
||||
|
<PlusButton on:click={onCreate} /> |
||||
|
{:else} |
||||
|
<IconButton icon="x" on:click={onRemoved} /> |
||||
|
{/if} |
||||
|
{/if} |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1 @@ |
|||||
|
export { default } from "./EventsEditor.svelte"; |
||||
@ -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) |
||||
|
}) |
||||
|
}) |
||||
@ -1,12 +0,0 @@ |
|||||
{ |
|
||||
"env": { |
|
||||
"browser": true, |
|
||||
"es6": true, |
|
||||
"jest": true |
|
||||
}, |
|
||||
"extends": "eslint:recommended", |
|
||||
"parser": "babel-eslint", |
|
||||
"parserOptions": { |
|
||||
"sourceType": "module" |
|
||||
} |
|
||||
} |
|
||||
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
File diff suppressed because it is too large
Loading…
Reference in new issue