forked from tsai/budibase
62 changed files with 2363 additions and 786 deletions
@ -0,0 +1,152 @@ |
|||||
|
static_resources: |
||||
|
listeners: |
||||
|
- name: main_listener |
||||
|
address: |
||||
|
socket_address: { address: 0.0.0.0, port_value: 10000 } |
||||
|
filter_chains: |
||||
|
- filters: |
||||
|
- name: envoy.filters.network.http_connection_manager |
||||
|
typed_config: |
||||
|
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager |
||||
|
stat_prefix: ingress |
||||
|
codec_type: auto |
||||
|
route_config: |
||||
|
name: local_route |
||||
|
virtual_hosts: |
||||
|
- name: local_services |
||||
|
domains: ["*"] |
||||
|
routes: |
||||
|
- match: { prefix: "/app/" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
prefix_rewrite: "/" |
||||
|
|
||||
|
- match: { path: "/v1/update" } |
||||
|
route: |
||||
|
cluster: watchtower-service |
||||
|
|
||||
|
- match: { prefix: "/builder/" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
|
||||
|
- match: { prefix: "/builder" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
|
||||
|
- match: { prefix: "/app_" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
|
||||
|
# special cases for worker admin (deprecated), global and system API |
||||
|
- match: { prefix: "/api/global/" } |
||||
|
route: |
||||
|
cluster: worker-service |
||||
|
|
||||
|
- match: { prefix: "/api/admin/" } |
||||
|
route: |
||||
|
cluster: worker-service |
||||
|
|
||||
|
- match: { prefix: "/api/system/" } |
||||
|
route: |
||||
|
cluster: worker-service |
||||
|
|
||||
|
- match: { path: "/" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
|
||||
|
# special case for when API requests are made, can just forward, not to minio |
||||
|
- match: { prefix: "/api/" } |
||||
|
route: |
||||
|
cluster: app-service |
||||
|
timeout: 120s |
||||
|
|
||||
|
- match: { prefix: "/worker/" } |
||||
|
route: |
||||
|
cluster: worker-service |
||||
|
prefix_rewrite: "/" |
||||
|
|
||||
|
- match: { prefix: "/db/" } |
||||
|
route: |
||||
|
cluster: couchdb-service |
||||
|
prefix_rewrite: "/" |
||||
|
|
||||
|
# minio is on the default route because this works |
||||
|
# best, minio + AWS SDK doesn't handle path proxy |
||||
|
- match: { prefix: "/" } |
||||
|
route: |
||||
|
cluster: minio-service |
||||
|
|
||||
|
http_filters: |
||||
|
- name: envoy.filters.http.router |
||||
|
|
||||
|
clusters: |
||||
|
- name: app-service |
||||
|
connect_timeout: 0.25s |
||||
|
type: strict_dns |
||||
|
lb_policy: round_robin |
||||
|
load_assignment: |
||||
|
cluster_name: app-service |
||||
|
endpoints: |
||||
|
- lb_endpoints: |
||||
|
- endpoint: |
||||
|
address: |
||||
|
socket_address: |
||||
|
address: app-service |
||||
|
port_value: 4002 |
||||
|
|
||||
|
- name: minio-service |
||||
|
connect_timeout: 0.25s |
||||
|
type: strict_dns |
||||
|
lb_policy: round_robin |
||||
|
load_assignment: |
||||
|
cluster_name: minio-service |
||||
|
endpoints: |
||||
|
- lb_endpoints: |
||||
|
- endpoint: |
||||
|
address: |
||||
|
socket_address: |
||||
|
address: minio-service |
||||
|
port_value: 9000 |
||||
|
|
||||
|
- name: worker-service |
||||
|
connect_timeout: 0.25s |
||||
|
type: strict_dns |
||||
|
lb_policy: round_robin |
||||
|
load_assignment: |
||||
|
cluster_name: worker-service |
||||
|
endpoints: |
||||
|
- lb_endpoints: |
||||
|
- endpoint: |
||||
|
address: |
||||
|
socket_address: |
||||
|
address: worker-service |
||||
|
port_value: 4003 |
||||
|
|
||||
|
- name: couchdb-service |
||||
|
connect_timeout: 0.25s |
||||
|
type: strict_dns |
||||
|
lb_policy: round_robin |
||||
|
load_assignment: |
||||
|
cluster_name: couchdb-service |
||||
|
endpoints: |
||||
|
- lb_endpoints: |
||||
|
- endpoint: |
||||
|
address: |
||||
|
socket_address: |
||||
|
address: couchdb-service |
||||
|
port_value: 5984 |
||||
|
|
||||
|
- name: watchtower-service |
||||
|
connect_timeout: 0.25s |
||||
|
type: strict_dns |
||||
|
lb_policy: round_robin |
||||
|
load_assignment: |
||||
|
cluster_name: watchtower-service |
||||
|
endpoints: |
||||
|
- lb_endpoints: |
||||
|
- endpoint: |
||||
|
address: |
||||
|
socket_address: |
||||
|
address: watchtower-service |
||||
|
port_value: 8080 |
||||
|
|
||||
@ -0,0 +1,178 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Button, |
||||
|
Icon, |
||||
|
DrawerContent, |
||||
|
Layout, |
||||
|
Input, |
||||
|
Select, |
||||
|
Label, |
||||
|
Body, |
||||
|
} from "@budibase/bbui" |
||||
|
import { flip } from "svelte/animate" |
||||
|
import { dndzone } from "svelte-dnd-action" |
||||
|
import { generate } from "shortid" |
||||
|
|
||||
|
export let columns = [] |
||||
|
export let options = [] |
||||
|
|
||||
|
const flipDurationMs = 150 |
||||
|
let dragDisabled = true |
||||
|
|
||||
|
$: unselectedColumns = getUnselectedColumns(options, columns) |
||||
|
$: columns.forEach(column => { |
||||
|
if (!column.id) { |
||||
|
column.id = generate() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const getUnselectedColumns = (allColumns, selectedColumns) => { |
||||
|
let optionsObj = {} |
||||
|
allColumns.forEach(option => { |
||||
|
optionsObj[option] = true |
||||
|
}) |
||||
|
selectedColumns?.forEach(column => { |
||||
|
delete optionsObj[column.name] |
||||
|
}) |
||||
|
return Object.keys(optionsObj) |
||||
|
} |
||||
|
|
||||
|
const getRemainingColumnOptions = selectedColumn => { |
||||
|
if (!selectedColumn || unselectedColumns.includes(selectedColumn)) { |
||||
|
return unselectedColumns |
||||
|
} |
||||
|
return [selectedColumn, ...unselectedColumns] |
||||
|
} |
||||
|
|
||||
|
const addColumn = () => { |
||||
|
columns = [...columns, {}] |
||||
|
} |
||||
|
|
||||
|
const removeColumn = id => { |
||||
|
columns = columns.filter(column => column.id !== id) |
||||
|
} |
||||
|
|
||||
|
const updateColumnOrder = e => { |
||||
|
columns = e.detail.items |
||||
|
} |
||||
|
|
||||
|
const handleFinalize = e => { |
||||
|
updateColumnOrder(e) |
||||
|
dragDisabled = true |
||||
|
} |
||||
|
|
||||
|
const reset = () => { |
||||
|
columns = options.map(col => ({ |
||||
|
name: col, |
||||
|
displayName: col, |
||||
|
})) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<DrawerContent> |
||||
|
<div class="container"> |
||||
|
<Layout noPadding gap="S"> |
||||
|
{#if columns?.length} |
||||
|
<Layout noPadding gap="XS"> |
||||
|
<div class="column"> |
||||
|
<div /> |
||||
|
<Label size="L">Column</Label> |
||||
|
<Label size="L">Label</Label> |
||||
|
<div /> |
||||
|
</div> |
||||
|
<div |
||||
|
class="columns" |
||||
|
use:dndzone={{ |
||||
|
items: columns, |
||||
|
flipDurationMs, |
||||
|
dropTargetStyle: { outline: "none" }, |
||||
|
dragDisabled, |
||||
|
}} |
||||
|
on:finalize={handleFinalize} |
||||
|
on:consider={updateColumnOrder} |
||||
|
> |
||||
|
{#each columns as column (column.id)} |
||||
|
<div class="column" animate:flip={{ duration: flipDurationMs }}> |
||||
|
<div |
||||
|
class="handle" |
||||
|
aria-label="drag-handle" |
||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"} |
||||
|
on:mousedown={() => (dragDisabled = false)} |
||||
|
> |
||||
|
<Icon name="DragHandle" size="XL" /> |
||||
|
</div> |
||||
|
<Select |
||||
|
bind:value={column.name} |
||||
|
placeholder="Column" |
||||
|
options={getRemainingColumnOptions(column.name)} |
||||
|
on:change={e => (column.displayName = e.detail)} |
||||
|
/> |
||||
|
<Input bind:value={column.displayName} placeholder="Label" /> |
||||
|
<Icon |
||||
|
name="Close" |
||||
|
hoverable |
||||
|
size="S" |
||||
|
on:click={() => removeColumn(column.id)} |
||||
|
disabled={columns.length === 1} |
||||
|
/> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
</Layout> |
||||
|
{:else} |
||||
|
<div class="column"> |
||||
|
<div /> |
||||
|
<Body size="S">Add the first column to your table.</Body> |
||||
|
</div> |
||||
|
{/if} |
||||
|
<div class="columns"> |
||||
|
<div class="column"> |
||||
|
<div /> |
||||
|
<div class="buttons"> |
||||
|
<Button secondary icon="Add" on:click={addColumn}> |
||||
|
Add column |
||||
|
</Button> |
||||
|
<Button secondary quiet on:click={reset}>Reset columns</Button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</DrawerContent> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
gap: var(--spacing-m); |
||||
|
} |
||||
|
.column { |
||||
|
gap: var(--spacing-l); |
||||
|
display: grid; |
||||
|
grid-template-columns: 20px 1fr 1fr 20px; |
||||
|
align-items: center; |
||||
|
border-radius: var(--border-radius-s); |
||||
|
transition: background-color ease-in-out 130ms; |
||||
|
} |
||||
|
.column:hover { |
||||
|
background-color: var(--spectrum-global-color-gray-100); |
||||
|
} |
||||
|
.handle { |
||||
|
display: grid; |
||||
|
place-items: center; |
||||
|
} |
||||
|
.buttons { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-m); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,64 @@ |
|||||
|
<script> |
||||
|
import { Button, ActionButton, Drawer } from "@budibase/bbui" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
import ColumnDrawer from "./ColumnDrawer.svelte" |
||||
|
import { cloneDeep } from "lodash/fp" |
||||
|
import { |
||||
|
getDatasourceForProvider, |
||||
|
getSchemaForDatasource, |
||||
|
} from "builderStore/dataBinding" |
||||
|
import { currentAsset } from "builderStore" |
||||
|
|
||||
|
export let componentInstance |
||||
|
export let value = [] |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
let drawer |
||||
|
let boundValue |
||||
|
|
||||
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) |
||||
|
$: schema = getSchemaForDatasource($currentAsset, datasource).schema |
||||
|
$: options = Object.keys(schema || {}) |
||||
|
$: sanitisedValue = getValidColumns(value, options) |
||||
|
$: updateBoundValue(sanitisedValue) |
||||
|
|
||||
|
const updateBoundValue = value => { |
||||
|
boundValue = cloneDeep(value) |
||||
|
} |
||||
|
|
||||
|
const getValidColumns = (columns, options) => { |
||||
|
// If no columns then default to all columns |
||||
|
if (!Array.isArray(columns) || !columns.length) { |
||||
|
return options.map(col => ({ |
||||
|
name: col, |
||||
|
displayName: col, |
||||
|
})) |
||||
|
} |
||||
|
// We need to account for legacy configs which would just be an array |
||||
|
// of strings |
||||
|
if (typeof columns[0] === "string") { |
||||
|
columns = columns.map(col => ({ |
||||
|
name: col, |
||||
|
displayName: col, |
||||
|
})) |
||||
|
} |
||||
|
return columns.filter(column => { |
||||
|
return options.includes(column.name) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const save = () => { |
||||
|
dispatch("change", getValidColumns(boundValue, options)) |
||||
|
drawer.hide() |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<ActionButton on:click={drawer.show}>Configure columns</ActionButton> |
||||
|
<Drawer bind:this={drawer} title="Table Columns"> |
||||
|
<svelte:fragment slot="description"> |
||||
|
Configure the columns in your table. |
||||
|
</svelte:fragment> |
||||
|
<Button cta slot="buttons" on:click={save}>Save</Button> |
||||
|
<ColumnDrawer slot="body" bind:columns={boundValue} {options} /> |
||||
|
</Drawer> |
||||
File diff suppressed because it is too large
@ -1,2 +1,3 @@ |
|||||
dist/ |
dist/ |
||||
node_modules/ |
node_modules/ |
||||
|
stats.html |
||||
Loading…
Reference in new issue