UI structure

This commit is contained in:
Martin McKeaveney 2020-03-12 14:23:29 +00:00
parent 8c983192e6
commit 4678f2c168
29 changed files with 576 additions and 212 deletions

View File

@ -47,6 +47,7 @@
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"shortid": "^2.2.8", "shortid": "^2.2.8",
"string_decoder": "^1.2.0", "string_decoder": "^1.2.0",
"svelte-routing": "^1.4.2",
"uikit": "^3.1.7" "uikit": "^3.1.7"
}, },
"devDependencies": { "devDependencies": {

View File

@ -165,7 +165,7 @@ export default {
svelte({ svelte({
// enable run-time checks when not in production // enable run-time checks when not in production
dev: !production, dev: !production,
include: "src/**/*.svelte", include: ["src/**/*.svelte", "node_modules/**/*.svelte"],
// we'll extract any component CSS out into // we'll extract any component CSS out into
// a separate file — better for performance // a separate file — better for performance
css: css => { css: css => {

View File

@ -29,16 +29,16 @@
{/await} {/await}
<!-- <!--
<div class="settings"> <div class="settings">
<IconButton icon="settings" <IconButton icon="settings"
on:click={store.showSettings}/> on:click={store.showSettings}/>
</div> </div>
{#if $store.useAnalytics} {#if $store.useAnalytics}
<iframe src="https://marblekirby.github.io/bb-analytics.html" width="0" height="0" style="visibility:hidden;display:none"/> <iframe src="https://marblekirby.github.io/bb-analytics.html" width="0" height="0" style="visibility:hidden;display:none"/>
{/if} {/if}
--> -->
</main> </main>
<style> <style>

View File

@ -8,11 +8,9 @@
import ComingSoon from "./common/ComingSoon.svelte" import ComingSoon from "./common/ComingSoon.svelte"
import { store } from "./builderStore" import { store } from "./builderStore"
import { setContext } from 'svelte'; import { setContext } from "svelte"
let activeNav = "database"; let activeNav = "database"
setContext("activeNav", activeNav);
</script> </script>
<div class="root"> <div class="root">
@ -20,14 +18,13 @@
<BackendNav /> <BackendNav />
</div> </div>
<div class="content"> <div class="content">
<!-- {#if activeNav === 'database'} --> <Database />
<Database /> </div>
<!-- {:else if activeNav === 'actions'} <!-- {:else if activeNav === 'actions'}
<ActionsAndTriggers /> <ActionsAndTriggers />
{:else if activeNav === 'access levels'} {:else if activeNav === 'access levels'}
<AccessLevels /> <AccessLevels />
{/if} --> {/if} -->
</div>
<div class="nav"> <div class="nav">
<SchemaManagementDrawer /> <SchemaManagementDrawer />
</div> </div>

View File

@ -98,4 +98,24 @@
background: #fff; background: #fff;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05); box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05);
}
.budibase__table {
border: 1px solid #ccc;
background: #fff;
border-radius: 2px;
}
.budibase__table thead {
background: var(--background-button);
}
.budibase__table th {
color: var(--button-text);
text-transform: capitalize;
font-weight: 500;
}
.budibase__table tr {
border-bottom: 1px solid #ccc;
} }

View File

@ -148,8 +148,6 @@ export const getStore = () => {
store.addStylesheet = addStylesheet(store) store.addStylesheet = addStylesheet(store)
store.removeStylesheet = removeStylesheet(store) store.removeStylesheet = removeStylesheet(store)
store.savePage = savePage(store) store.savePage = savePage(store)
// store.showFrontend = showFrontend(store)
// store.showBackend = showBackend(store)
store.showSettings = showSettings(store) store.showSettings = showSettings(store)
store.useAnalytics = useAnalytics(store) store.useAnalytics = useAnalytics(store)
store.createGeneratedComponents = createGeneratedComponents(store) store.createGeneratedComponents = createGeneratedComponents(store)

View File

@ -2,24 +2,17 @@
import { JavaScriptIcon } from "../common/Icons" import { JavaScriptIcon } from "../common/Icons"
// todo: use https://ace.c9.io // todo: use https://ace.c9.io
export let text = "" export let text = ""
export let label = ""
export let javascript = false
</script> </script>
<div class="header">
{#if javascript}
<JavaScriptIcon />
{/if}
<span>{label}</span>
</div>
<textarea class="uk-textarea" bind:value={text} /> <textarea class="uk-textarea" bind:value={text} />
<style> <style>
textarea { textarea {
padding: 3px; padding: 10px;
margin-top: 5px; margin-top: 5px;
margin-bottom: 10px; margin-bottom: 10px;
background: var(--lightslate); background: var(--primary100);
color: var(--white);
font-family: "Courier New", Courier, monospace; font-family: "Courier New", Courier, monospace;
width: 95%; width: 95%;
height: 100px; height: 100px;

View File

@ -1,10 +1,18 @@
<script> <script>
import getIcon from "./icon" import getIcon from "./icon"
export let icon
export let value export let value
</script> </script>
<div class="select-container"> <div class="select-container">
<select on:change bind:value> {#if icon}
<i class={icon} />
{/if}
<select
class:adjusted={icon}
on:change bind:value
>
<slot /> <slot />
</select> </select>
<span class="arrow"> <span class="arrow">
@ -22,6 +30,16 @@
min-width: 200px; min-width: 200px;
} }
.adjusted {
padding-left: 2.5em;
}
i {
position: absolute;
left: 8px;
top: 8px;
}
select { select {
height: 35px; height: 35px;
display: block; display: block;

View File

@ -0,0 +1,45 @@
<script>
export let data = []
export let headers = []
</script>
<table class="uk-table">
<thead>
<tr>
<th>Edit</th>
{#each headers as header}
<th>{header}</th>
{/each}
</tr>
</thead>
<tbody>
{#each data as row}
<tr>
<td>
<div class="uk-inline">
<i class="ri-more-line" />
<div uk-dropdown="mode: click">
<ul class="uk-nav uk-dropdown-nav">
<li>
<div>View</div>
</li>
<li>
<div>Edit</div>
</li>
<li>
<div on:click={() => (deleteRecordModal = true)}>Delete</div>
</li>
<li>
<div>Duplicate</div>
</li>
</ul>
</div>
</div>
</td>
{#each headers as header}
<td>{row[header]}</td>
{/each}
</tr>
{/each}
</tbody>
</table>

View File

@ -1,12 +1,13 @@
<script> <script>
import HierarchyRow from "./HierarchyRow.svelte" import HierarchyRow from "./HierarchyRow.svelte"
import RecordView from "./RecordView.svelte" import ModelView from "./ModelView.svelte"
import IndexView from "./IndexView.svelte" import IndexView from "./IndexView.svelte"
import ModelDataTable from "./ModelDataTable" import ModelDataTable from "./ModelDataTable"
import ActionsHeader from "./ActionsHeader.svelte" import ActionsHeader from "./ActionsHeader.svelte"
import { store } from "../builderStore" import { store } from "../builderStore"
import getIcon from "../common/icon" import getIcon from "../common/icon"
import DropdownButton from "../common/DropdownButton.svelte" import DropdownButton from "../common/DropdownButton.svelte"
import Modal from "../common/Modal.svelte"
import { hierarchy as hierarchyFunctions } from "../../../core/src" import { hierarchy as hierarchyFunctions } from "../../../core/src"
const hierarchyWidth = "200px" const hierarchyWidth = "200px"
@ -53,18 +54,24 @@
</script> </script>
<div class="root"> <div class="root">
<div class="actions-header"> <!-- <div class="actions-header">
{#if $store.currentNode} {#if $store.currentNode}
<ActionsHeader left={hierarchyWidth} /> <ActionsHeader />
{/if} {/if}
</div> </div> -->
<div class="node-view"> <div class="node-view">
{#if !$store.currentNode} <div class="breadcrumbs">{$store.currentlySelectedDatabase}</div>
<ModelDataTable /> <ModelDataTable />
{:else if $store.currentNode.type === 'record'} {#if $store.currentNode}
<RecordView /> <Modal isOpen={$store.currentNode}>
{:else} {#if $store.currentNode.type === 'record'}
<IndexView /> <ModelView />
<ActionsHeader />
{:else}
<IndexView />
<ActionsHeader />
{/if}
</Modal>
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -68,20 +68,14 @@
<ErrorsBox {errors} /> <ErrorsBox {errors} />
<form class="uk-form-horizontal"> <form class="uk-form-stacked">
<Textbox label="Name" bind:text={clonedField.name} />
<Dropdown <Dropdown
label="Type" label="Type"
bind:selected={clonedField.type} bind:selected={clonedField.type}
options={keys(allTypes)} options={keys(allTypes)}
on:change={typeChanged} /> on:change={typeChanged} />
{#if isNew}
<Textbox label="Field Name" bind:text={clonedField.name} />
{:else}
<div style="font-weight: bold">{clonedField.name}</div>
{/if}
<Textbox label="Label" bind:text={clonedField.label} /> <Textbox label="Label" bind:text={clonedField.label} />
{#if clonedField.type === 'string'} {#if clonedField.type === 'string'}
@ -89,7 +83,7 @@
label="Max Length" label="Max Length"
bind:value={clonedField.typeOptions.maxLength} /> bind:value={clonedField.typeOptions.maxLength} />
<ValuesList <ValuesList
label="Values (options)" label="Categories"
bind:values={clonedField.typeOptions.values} /> bind:values={clonedField.typeOptions.values} />
<Checkbox <Checkbox
label="Declared Values Only" label="Declared Values Only"

View File

@ -9,20 +9,30 @@
const pipe = common.$ const pipe = common.$
const SNIPPET_EDITORS = {
MAP: "Map",
FILTER: "Filter",
SHARD: "Shard Name",
}
let index let index
let indexableRecords = [] let indexableRecords = []
let currentSnippetEditor = SNIPPET_EDITORS.MAP
store.subscribe($store => { store.subscribe($store => {
index = $store.currentNode index = $store.currentNode
indexableRecords = pipe($store.hierarchy, [ indexableRecords = pipe(
hierarchyFunctions.getFlattenedHierarchy, $store.hierarchy,
filter(hierarchyFunctions.isDecendant(index.parent())), [
filter(hierarchyFunctions.isRecord), hierarchyFunctions.getFlattenedHierarchy,
map(n => ({ filter(hierarchyFunctions.isDecendant(index.parent())),
node: n, filter(hierarchyFunctions.isRecord),
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds), map(n => ({
})), node: n,
]) isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds),
})),
]
)
}) })
const toggleAllowedRecord = record => { const toggleAllowedRecord = record => {
@ -36,11 +46,28 @@
} }
</script> </script>
<form class="uk-form-horizontal root"> <h3 class="budibase__title--3">
<Textbox bind:text={index.name} label="Name" /> <i class="ri-eye-line" />
Create / Edit View
</h3>
<form class="uk-form-stacked root">
<h4 class="budibase__label--big">Settings</h4>
<div class="uk-grid-small" uk-grid>
<div class="uk-width-1-2@s">
<Textbox bind:text={index.name} label="Name" />
</div>
<div class="uk-width-1-2@s">
<Dropdown
label="View Type"
bind:selected={index.indexType}
options={['ancestor', 'reference']} />
</div>
</div>
<div class="allowed-records"> <div class="allowed-records">
<div class="index-label">Records to Index</div> <div class="budibase__label--big">
Which models would you like to add to this view?
</div>
{#each indexableRecords as rec} {#each indexableRecords as rec}
<input <input
type="checkbox" type="checkbox"
@ -50,14 +77,22 @@
{/each} {/each}
</div> </div>
<Dropdown <h4 class="budibase__label--big">Snippets</h4>
label="Index Type" {#each Object.values(SNIPPET_EDITORS) as snippetType}
bind:selected={index.indexType} <span
options={['ancestor', 'reference']} /> class="snippet-selector__heading hoverable"
class:highlighted={currentSnippetEditor === snippetType}
<CodeArea bind:text={index.map} javascript label="Map" /> on:click={() => (currentSnippetEditor = snippetType)}>
<CodeArea bind:text={index.filter} javascript label="Filter" /> {snippetType}
<CodeArea javascript bind:text={index.getShardName} label="Shard Name" /> </span>
{/each}
{#if currentSnippetEditor === SNIPPET_EDITORS.MAP}
<CodeArea bind:text={index.map} label="Map" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
<CodeArea bind:text={index.filter} label="Filter" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.SHARD}
<CodeArea bind:text={index.getShardName} label="Shard Name" />
{/if}
</form> </form>
@ -79,4 +114,13 @@
color: #333; color: #333;
font-size: 0.875rem; font-size: 0.875rem;
} }
.snippet-selector__heading {
margin-right: 20px;
opacity: 0.7;
}
.highlighted {
opacity: 1;
}
</style> </style>

View File

@ -1,6 +1,11 @@
<script> <script>
import { store } from "../../builderStore"
import Select from "../../common/Select.svelte" import Select from "../../common/Select.svelte"
import { CreateEditRecordModal, DeleteRecordModal } from "./modals"
import ActionButton from "../../common/ActionButton.svelte" import ActionButton from "../../common/ActionButton.svelte"
import TablePagination from "./TablePagination.svelte"
import * as api from "./api"
import { getIndexSchema } from "../../common/core"
let pages = [1, 2, 3] let pages = [1, 2, 3]
@ -10,14 +15,31 @@
{ name: "Martin", inStock: true }, { name: "Martin", inStock: true },
] ]
export let headers = ["name", "inStock"] export let headers = ["name", "inStock"]
export let pageSize = 10 // export let pageSize = 10
let selectedView = ""
let modalOpen = false
let deleteRecordModal = false
$: indexes = $store.hierarchy.indexes
const getSchema = getIndexSchema($store.hierarchy)
</script> </script>
<CreateEditRecordModal bind:modalOpen />
<DeleteRecordModal modalOpen={deleteRecordModal} />
<section> <section>
<h4 class="budibase__title--3">Shoe database</h4>
<div class="table-controls"> <div class="table-controls">
<Select /> <h4 class="budibase__title--3">Shoe database</h4>
<ActionButton primary>Create new record</ActionButton> <Select
icon="ri-eye-line"
on:change={e => api.fetchDataForView(e.target.value)}>
{#each indexes as index}
({console.log(getSchema(index))})
<option value={index.name}>{index.name}</option>
{/each}
</Select>
</div> </div>
<table class="uk-table"> <table class="uk-table">
<thead> <thead>
@ -32,7 +54,25 @@
{#each data as row} {#each data as row}
<tr> <tr>
<td> <td>
<i class="ri-more-line" /> <div class="uk-inline">
<i class="ri-more-line" />
<div uk-dropdown="mode: click">
<ul class="uk-nav uk-dropdown-nav">
<li>
<div>View</div>
</li>
<li>
<div on:click={() => (modalOpen = true)}>Edit</div>
</li>
<li>
<div on:click={() => (deleteRecordModal = true)}>Delete</div>
</li>
<li>
<div>Duplicate</div>
</li>
</ul>
</div>
</div>
</td> </td>
{#each headers as header} {#each headers as header}
<td>{row[header]}</td> <td>{row[header]}</td>
@ -41,13 +81,7 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
<div class="pagination"> <TablePagination {data} />
<button>Previous</button>
<button>Next</button>
<!-- {#each data as page}
<button>{page}</button>
{/each} -->
</div>
</section> </section>
<style> <style>
@ -76,4 +110,8 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.ri-more-line:hover, .uk-dropdown-nav li:hover{
cursor: pointer;
}
</style> </style>

View File

@ -0,0 +1,44 @@
<script>
export let data = []
</script>
<div class="pagination">
<div class="pagination__buttons">
<button>Previous</button>
<button>Next</button>
{#each data as page, idx}
<button>{idx + 1}</button>
{/each}
</div>
<p>Showing 10 (hardcoded, update this) of {data.length} entries</p>
</div>
<style>
.pagination {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.pagination__buttons button {
display: inline-block;
padding: 10px;
margin: 0;
background: #fff;
border: 1px solid #ccc;
text-transform: capitalize;
border-radius: 5px;
font-family: Roboto;
min-width: 20px;
}
.pagination__buttons button:hover {
cursor: pointer;
}
.ri-more-line:hover {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,24 @@
import api from "../../builderStore/api";
export async function deleteRecord(appName, appInstanceId, record) {
const DELETE_RECORDS_URL = `/_builder/instance/${appName}/${appInstanceId}/api/record/${record.name}/${record.id}`
const response = await api.delete({
url: DELETE_RECORDS_URL
});
}
export async function createNewRecord(record) {
console.log(record);
}
export async function fetchDataForView(viewName) {
console.log(viewName);
// const FETCH_RECORDS_URL = `/_builder/instance/${}/${}/api/listRecords/`
// const response = await api.get({ url: FETCH_RECORDS_URL });
// console.log(response);
// GET /_builder/instance/:appname/:instanceid/api/listRecords/contacts/abcd1234/all_deals
}

View File

@ -0,0 +1,23 @@
<script>
import Modal from "../../common/Modal.svelte"
import ActionButton from "../../common/ActionButton.svelte"
import * as api from "./api"
export let modalOpen = false
let recordInfo = {}
const onClosed = () => (modalOpen = false)
</script>
<Modal {onClosed} bind:isOpen={modalOpen} title={"Create / Edit Field"}>
<div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton
disabled={false}
on:click={() => api.createNewRecord(recordInfo)}>
Save
</ActionButton>
</div>
</Modal>

View File

@ -0,0 +1,23 @@
<script>
import Modal from "../../common/Modal.svelte"
import ActionButton from "../../common/ActionButton.svelte"
import * as api from "./api"
export let modalOpen = false
let recordInfo = {}
const onClosed = () => (modalOpen = false)
</script>
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
<h4 class="budibase__title--4">Create / Edit Record</h4>
<div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton
disabled={false}
on:click={() => api.createNewRecord(recordInfo)}>
Save
</ActionButton>
</div>
</Modal>

View File

@ -0,0 +1,60 @@
<script>
import Modal from "../../../common/Modal.svelte"
import ActionButton from "../../../common/ActionButton.svelte"
import * as api from "../api"
export let modalOpen = false
export let model = {}
const onClosed = () => (modalOpen = false)
</script>
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
<h4 class="budibase__title--4">Create / Edit Record</h4>
<div class="actions">
<form>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Text</label>
<div class="uk-form-controls">
<input
class="uk-input"
id="form-stacked-text"
type="text"
placeholder="Some text..." />
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-select">Select</label>
<div class="uk-form-controls">
<select class="uk-select" id="form-stacked-select">
<option>Option 01</option>
<option>Option 02</option>
</select>
</div>
</div>
<div class="uk-margin">
<div class="uk-form-label">Radio</div>
<div class="uk-form-controls">
<label>
<input class="uk-radio" type="radio" name="radio1" />
Option 01
</label>
<br />
<label>
<input class="uk-radio" type="radio" name="radio1" />
Option 02
</label>
</div>
</div>
</form>
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton
disabled={false}
on:click={() => api.createNewRecord(recordInfo)}>
Save
</ActionButton>
</div>
</Modal>

View File

@ -0,0 +1,23 @@
<script>
import Modal from "../../common/Modal.svelte"
import ActionButton from "../../common/ActionButton.svelte"
import * as api from "./api"
export let modalOpen = false
let recordInfo = {}
const onClosed = () => (modalOpen = false)
</script>
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
<div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton
disabled={false}
on:click={() => api.createNewRecord(recordInfo)}>
Save
</ActionButton>
</div>
</Modal>

View File

@ -0,0 +1,24 @@
<script>
import Modal from "../../../common/Modal.svelte"
import ActionButton from "../../../common/ActionButton.svelte"
import * as api from "../api"
export let modalOpen = false
let recordInfo = {}
const onClosed = () => (modalOpen = false)
</script>
<Modal {onClosed} bind:isOpen={modalOpen} title={"Delete Record?"}>
<h4 class="budibase__title--4">Delete Record</h4>
Are you sure you want to delete this record? All of your data will be permanently removed. This action cannot be undone.
<div class="modal-actions">
<ActionButton on:click={onClosed}>Cancel</ActionButton>
<ActionButton
alert
on:click={() => api.deleteRecord(recordInfo)}>
Delete
</ActionButton>
</div>
</Modal>

View File

@ -0,0 +1,2 @@
export { default as DeleteRecordModal } from "./DeleteRecord.svelte";
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte";

View File

@ -24,11 +24,14 @@
record = $store.currentNode record = $store.currentNode
const flattened = h.getFlattenedHierarchy($store.hierarchy) const flattened = h.getFlattenedHierarchy($store.hierarchy)
getIndexAllowedRecords = index => getIndexAllowedRecords = index =>
pipe(index.allowedRecordNodeIds, [ pipe(
filter(id => some(n => n.nodeId === id)(flattened)), index.allowedRecordNodeIds,
map(id => find(n => n.nodeId === id)(flattened).name), [
join(", "), filter(id => some(n => n.nodeId === id)(flattened)),
]) map(id => find(n => n.nodeId === id)(flattened).name),
join(", "),
]
)
newField = () => { newField = () => {
isNewField = true isNewField = true
@ -72,16 +75,19 @@
} }
let getTypeOptions = typeOptions => let getTypeOptions = typeOptions =>
pipe(typeOptions, [ pipe(
keys, typeOptions,
map( [
k => keys,
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText( map(
typeOptions[k] k =>
)}` `<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(
), typeOptions[k]
join("<br>"), )}`
]) ),
join("<br>"),
]
)
const nameChanged = ev => { const nameChanged = ev => {
const pluralName = n => `${n}s` const pluralName = n => `${n}s`
@ -93,68 +99,78 @@
<div class="root"> <div class="root">
<form class="uk-form-horizontal"> <form class="uk-form-stacked">
<h3 class="budibase__title--3">Settings</h3> <h3 class="budibase__title--3">
<i class="ri-list-settings-line" />
Create / Edit Model
</h3>
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} /> <h3 class="budibase__label--big">Settings</h3>
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
{#if !record.isSingle} {#if !record.isSingle}
<Textbox label="Collection Name:" bind:text={record.collectionName} /> <Textbox label="Collection Name" bind:text={record.collectionName} />
<Textbox <Textbox
label="Estimated Record Count:" label="Estimated Record Count"
bind:text={record.estimatedRecordCount} /> bind:text={record.estimatedRecordCount} />
{/if} {/if}
<div class="recordkey">{record.nodeKey()}</div> <div class="recordkey">{record.nodeKey()}</div>
</form> </form>
<h3 class="budibase__title--3"> <div class="table-controls">
<span class="budibase__label--big">Fields</span>
<h4 class="hoverable" on:click={newField}>Add new field</h4>
</div>
<!-- <h3 class="budibase__label--big">
Fields Fields
<span class="add-field-button" on:click={newField}> <span class="add-field-button" on:click={newField}>
{@html getIcon('plus')} {@html getIcon('plus')}
</span> </span>
</h3> </h3> -->
{#if record.fields.length > 0} <table class="fields-table uk-table budibase__table">
<table class="fields-table uk-table"> <thead>
<thead> <tr>
<th>Edit</th>
<th>Name</th>
<th>Type</th>
<th>Values</th>
<th />
</tr>
</thead>
<tbody>
{#each record.fields as field}
<tr> <tr>
<th>Name</th> <td>
<th>Type</th> <i class="ri-more-line" on:click={() => editField(field)} />
<th>Options</th> </td>
<th /> <td>
<div>{field.name}</div>
</td>
<td>{field.type}</td>
<td>({console.log(field.typeOptions)}) {field.typeOptions.values}</td>
<td>
<!-- <span class="edit-button" on:click={() => editField(field)}>
{@html getIcon('edit')}
</span> -->
<span class="edit-button" on:click={() => deleteField(field)}>
{@html getIcon('trash')}
</span>
</td>
</tr> </tr>
</thead> {/each}
<tbody> </tbody>
{#each record.fields as field} </table>
<tr>
<td>
<div class="field-label">{field.label}</div>
<div style="font-size: 0.8em; color: var(--slate)">
{field.name}
</div>
</td>
<td>{field.type}</td>
<td>
{@html getTypeOptions(field.typeOptions)}
</td>
<td>
<span class="edit-button" on:click={() => editField(field)}>
{@html getIcon('edit')}
</span>
<span class="edit-button" on:click={() => deleteField(field)}>
{@html getIcon('trash')}
</span>
</td>
</tr>
{/each}
</tbody>
</table>
{:else}(no fields added){/if}
{#if editingField} {#if editingField}
<Modal <Modal
title="Manage Index Fields"
bind:isOpen={editingField} bind:isOpen={editingField}
onClosed={() => onFinishedFieldEdit(false)}> onClosed={() => onFinishedFieldEdit(false)}>
<h3 class="budibase__title--3">
<i class="ri-file-list-line" />
Create / Edit Field
</h3>
<FieldView <FieldView
field={fieldToEdit} field={fieldToEdit}
onFinished={onFinishedFieldEdit} onFinished={onFinishedFieldEdit}
@ -163,9 +179,9 @@
</Modal> </Modal>
{/if} {/if}
<h3 class="budibase__title--3">Indexes</h3> <!-- <h3 class="budibase__title--3">Indexes</h3> -->
{#each record.indexes as index} <!-- {#each record.indexes as index}
<div class="index-container"> <div class="index-container">
<div class="index-name"> <div class="index-name">
{index.name} {index.name}
@ -192,7 +208,7 @@
</div> </div>
{:else} {:else}
<div class="no-indexes">No indexes added.</div> <div class="no-indexes">No indexes added.</div>
{/each} {/each} -->
</div> </div>
@ -213,10 +229,6 @@
border-collapse: collapse; border-collapse: collapse;
} }
.add-field-button {
cursor: pointer;
}
.edit-button { .edit-button {
cursor: pointer; cursor: pointer;
color: var(--secondary25); color: var(--secondary25);
@ -227,35 +239,6 @@
color: var(--secondary75); color: var(--secondary75);
} }
th {
text-align: left;
}
td {
padding: 1rem 5rem 1rem 0rem;
margin: 0;
font-size: 14px;
font-weight: 500;
}
.field-label {
font-size: 14px;
font-weight: 500;
}
thead > tr {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--secondary75);
margin-bottom: 20px;
}
tbody > tr {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--primary10);
}
tbody > tr:hover { tbody > tr:hover {
background-color: var(--primary10); background-color: var(--primary10);
} }
@ -264,38 +247,17 @@
color: var(--secondary75); color: var(--secondary75);
} }
.index-container { .table-controls {
border-style: solid; display: flex;
border-width: 0 0 1px 0; justify-content: space-between;
border-color: var(--secondary25); align-items: center;
padding: 10px;
margin-bottom: 5px;
} }
.index-label { .ri-more-line:hover {
color: var(--slate); cursor: pointer;
} }
.index-name { h4 {
font-weight: bold;
color: var(--primary100);
}
.index-container code {
margin: 0; margin: 0;
display: inline;
background-color: var(--primary10);
color: var(--secondary100);
padding: 3px;
}
.index-field-row {
margin: 1rem 0rem 0rem 0rem;
}
.no-indexes {
margin: 1rem 0rem 0rem 0rem;
font-family: var(--fontnormal);
font-size: 14px;
} }
</style> </style>

View File

@ -102,4 +102,8 @@ h5 {
font-family: var(--fontblack); font-family: var(--fontblack);
font-size: 12pt; font-size: 12pt;
color: var(--darkslate); color: var(--darkslate);
}
.hoverable:hover {
cursor: pointer;
} }

View File

@ -15,5 +15,5 @@ import "codemirror/theme/monokai.css"
/* eslint-disable */ /* eslint-disable */
const app = new App({ const app = new App({
target: document.getElementById("app"), target: document.getElementById("app")
}) })

View File

@ -1,5 +1,5 @@
<script> <script>
import { setContext } from "svelte" import { getContext } from "svelte"
import { store } from "../builderStore" import { store } from "../builderStore"
import HierarchyRow from "./HierarchyRow.svelte" import HierarchyRow from "./HierarchyRow.svelte"
import DatabasesList from "./DatabasesList.svelte" import DatabasesList from "./DatabasesList.svelte"
@ -35,7 +35,7 @@
// let newChildActions = defaultNewChildActions // let newChildActions = defaultNewChildActions
const setActiveNav = name => () => setContext("activeNav", name) const setActiveNav = name => () => getContext("navigation").setActiveNav(name);
// store.subscribe(db => { // store.subscribe(db => {
// if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { // if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) {

View File

@ -1,9 +1,18 @@
<script> <script>
import { navigate } from "svelte-routing"
import { store } from "../builderStore" import { store } from "../builderStore"
import getIcon from "../common/icon" import getIcon from "../common/icon"
import { CheckIcon } from "../common/Icons" import { CheckIcon } from "../common/Icons"
$: instances = $store.appInstances $: instances = $store.appInstances
function selectDatabase(databaseId) {
store.update(state => {
state.currentlySelectedDatabase = databaseId
return state
})
navigate("/database", { replace: true })
}
</script> </script>
<div class="root"> <div class="root">
@ -11,14 +20,14 @@
{#each $store.appInstances as { id, name }} {#each $store.appInstances as { id, name }}
<li> <li>
<span class="icon"> <span class="icon">
{#if id === $store.currentPageName} {#if id === $store.currentlySelectedDatabase}
<CheckIcon /> <CheckIcon />
{/if} {/if}
</span> </span>
<button <button
class:active={id === $store.currentPageName} class:active={id === $store.currentlySelectedDatabase}
on:click={() => store.setCurrentPage(id)}> on:click={() => selectDatabase(id)}>
{name} {name}
</button> </button>
</li> </li>

View File

@ -16,8 +16,7 @@
store.subscribe(state => { store.subscribe(state => {
if (state.currentNode) { if (state.currentNode) {
navActive = navActive = node.nodeId === state.currentNode.nodeId
getContext("activeNav") === "database" && node.nodeId === state.currentNode.nodeId
} }
}) })
</script> </script>

View File

@ -70,7 +70,19 @@
<div class="components-list-container"> <div class="components-list-container">
<div class="nav-group-header"> <div class="nav-group-header">
<div class="hierarchy-title">Schema</div> <div class="hierarchy-title">Schema</div>
<DropdownButton iconName="plus" actions={newChildActions} /> <div class="uk-inline">
<i class="ri-add-line" />
<div uk-dropdown="mode: click">
<ul class="uk-nav uk-dropdown-nav">
<li>
Model
</li>
<li>
View
</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -44,7 +44,7 @@ export const generateSchema = (hierarchy, indexNode) => {
keys, keys,
map(k => ({ name: k, type: schema[k].name })), map(k => ({ name: k, type: schema[k].name })),
filter(s => s.name !== "sortKey"), filter(s => s.name !== "sortKey"),
orderBy("name", ["desc"]), // reverse aplha orderBy("name", ["desc"]), // reverse alpha
concat([{ name: "sortKey", type: all.string.name }]), // sortKey on end concat([{ name: "sortKey", type: all.string.name }]), // sortKey on end
reverse, // sortKey first, then rest are alphabetical reverse, // sortKey first, then rest are alphabetical
]) ])