mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-27 04:25:59 +00:00
forms
This commit is contained in:
@@ -149,7 +149,7 @@ module.exports = {
|
|||||||
return content.commands[command](args);
|
return content.commands[command](args);
|
||||||
},
|
},
|
||||||
|
|
||||||
authTypes_meta: 'post',
|
authTypes_meta: 'get',
|
||||||
async authTypes({ engine }) {
|
async authTypes({ engine }) {
|
||||||
const packageName = extractPackageName(engine);
|
const packageName = extractPackageName(engine);
|
||||||
const content = requirePlugin(packageName);
|
const content = requirePlugin(packageName);
|
||||||
|
|||||||
1
packages/types/engines.d.ts
vendored
1
packages/types/engines.d.ts
vendored
@@ -27,6 +27,7 @@ export interface EngineAuthType {
|
|||||||
export interface EngineDriver {
|
export interface EngineDriver {
|
||||||
engine: string;
|
engine: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
defaultPort?: number;
|
||||||
connect({ server, port, user, password, database }): any;
|
connect({ server, port, user, password, database }): any;
|
||||||
query(pool: any, sql: string): Promise<QueryResult>;
|
query(pool: any, sql: string): Promise<QueryResult>;
|
||||||
stream(pool: any, sql: string, options: StreamOptions);
|
stream(pool: any, sql: string, options: StreamOptions);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
--dim-statusbar-height: 20px;
|
--dim-statusbar-height: 20px;
|
||||||
--dim-left-panel-width: 300px;
|
--dim-left-panel-width: 300px;
|
||||||
--dim-tabs-panel-height: 53px;
|
--dim-tabs-panel-height: 53px;
|
||||||
|
--dim-tabs-height: 33px;
|
||||||
--dim-splitter-thickness: 3px;
|
--dim-splitter-thickness: 3px;
|
||||||
|
|
||||||
--dim-visible-left-panel: 1; /* set from JS */
|
--dim-visible-left-panel: 1; /* set from JS */
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
--dim-toolbar-height: 30px;
|
--dim-toolbar-height: 30px;
|
||||||
--dim-header-top: calc(var(--dim-toolbar-height) * var(--dim-visible-toolbar));
|
--dim-header-top: calc(var(--dim-toolbar-height) * var(--dim-visible-toolbar));
|
||||||
--dim-content-top: calc(var(--dim-header-top) + var(--dim-tabs-panel-height));
|
--dim-content-top: calc(var(--dim-header-top) + var(--dim-tabs-panel-height));
|
||||||
|
|
||||||
|
--dim-large-form-margin: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,66 +56,26 @@ body {
|
|||||||
max-width: 25%;
|
max-width: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* html, body {
|
.largeFormMarker input[type='text'] {
|
||||||
position: relative;
|
width: 100%;
|
||||||
width: 100%;
|
padding: 10px 10px;
|
||||||
height: 100%;
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
.largeFormMarker input[type='password'] {
|
||||||
color: #333;
|
width: 100%;
|
||||||
margin: 0;
|
padding: 10px 10px;
|
||||||
padding: 8px;
|
font-size: 14px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.largeFormMarker select {
|
||||||
color: rgb(0,100,200);
|
width: 100%;
|
||||||
text-decoration: none;
|
padding: 10px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: rgb(0,80,160);
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, button, select, textarea {
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
-webkit-padding: 0.4em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
margin: 0 0 0.5em 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
color: #333;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:not(:disabled):active {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus {
|
|
||||||
border-color: #666;
|
|
||||||
} */
|
|
||||||
|
|||||||
1
packages/web/src/forms/CheckboxField.svelte
Normal file
1
packages/web/src/forms/CheckboxField.svelte
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<input type="checkbox" {...$$restProps} />
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import FormStyledButton from '../widgets/FormStyledButton.svelte';
|
import FormStyledButton from '../widgets/FormStyledButton.svelte';
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const { values } = getFormContext();
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
dispatch('click', $values);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormStyledButton type="button" on:click {...$$props} />
|
<FormStyledButton type="button" on:click={handleClick} {...$$props} />
|
||||||
|
|||||||
14
packages/web/src/forms/FormCheckboxField.svelte
Normal file
14
packages/web/src/forms/FormCheckboxField.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import FromCheckboxFieldRaw from './FromTextFieldRaw.svelte';
|
||||||
|
|
||||||
|
export let label;
|
||||||
|
export let name;
|
||||||
|
export let templateProps = {};
|
||||||
|
|
||||||
|
const { template } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:component this={template} type="checkbox" {label} {...templateProps}>
|
||||||
|
<FromCheckboxFieldRaw {name} {...$$restProps} />
|
||||||
|
</svelte:component>
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import FormProviderCore from './FormProviderCore.svelte';
|
import FormProviderCore from './FormProviderCore.svelte';
|
||||||
|
|
||||||
export let initivalValues = {};
|
export let initialValues = {};
|
||||||
|
export let template;
|
||||||
|
|
||||||
const values = writable(initivalValues);
|
const values = writable(initialValues);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProviderCore {values}>
|
<FormProviderCore {values} {template}>
|
||||||
<slot />
|
<slot />
|
||||||
</FormProviderCore>
|
</FormProviderCore>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import { getContext, setContext } from 'svelte';
|
import { getContext, setContext } from 'svelte';
|
||||||
|
|
||||||
const contextKey = 'formProviderContextKey';
|
const contextKey = 'formProviderContextKey';
|
||||||
|
|
||||||
export function getFormContext(): any {
|
export function getFormContext(): any {
|
||||||
@@ -11,9 +12,16 @@
|
|||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
|
|
||||||
export let values;
|
export let values;
|
||||||
|
export let template;
|
||||||
|
|
||||||
|
const setFieldValue = (name, value) => {
|
||||||
|
values.update(x => ({ ...x, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
values,
|
values,
|
||||||
|
template,
|
||||||
|
setFieldValue,
|
||||||
submitActionRef: { current: null },
|
submitActionRef: { current: null },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
14
packages/web/src/forms/FormSelectField.svelte
Normal file
14
packages/web/src/forms/FormSelectField.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import FromSelectFieldRaw from './FromSelectFieldRaw.svelte';
|
||||||
|
|
||||||
|
export let label;
|
||||||
|
export let name;
|
||||||
|
export let templateProps = {};
|
||||||
|
|
||||||
|
const { template } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:component this={template} type="select" {label} {...templateProps}>
|
||||||
|
<FromSelectFieldRaw {name} {...$$restProps} />
|
||||||
|
</svelte:component>
|
||||||
@@ -6,10 +6,15 @@
|
|||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const { submitActionRef } = getFormContext();
|
const { submitActionRef } = getFormContext();
|
||||||
|
const { values } = getFormContext();
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
dispatch('click', $values);
|
||||||
|
}
|
||||||
|
|
||||||
submitActionRef.current = () => {
|
submitActionRef.current = () => {
|
||||||
dispatch('click');
|
handleClick();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormStyledButton type="submit" on:click {...$$props} />
|
<FormStyledButton type="submit" on:click={handleClick} {...$$props} />
|
||||||
|
|||||||
14
packages/web/src/forms/FormTextField.svelte
Normal file
14
packages/web/src/forms/FormTextField.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import FromTextFieldRaw from './FromTextFieldRaw.svelte';
|
||||||
|
|
||||||
|
export let label;
|
||||||
|
export let name;
|
||||||
|
export let templateProps = {};
|
||||||
|
|
||||||
|
const { template } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:component this={template} type="text" {label} {...templateProps}>
|
||||||
|
<FromTextFieldRaw {name} {...$$restProps} />
|
||||||
|
</svelte:component>
|
||||||
10
packages/web/src/forms/FromCheckboxFieldRaw.svelte
Normal file
10
packages/web/src/forms/FromCheckboxFieldRaw.svelte
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let name;
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextField {...$$restProps} value={$values[name]} on:change={e => setFieldValue(name, e.target['checked'])} />
|
||||||
10
packages/web/src/forms/FromSelectFieldRaw.svelte
Normal file
10
packages/web/src/forms/FromSelectFieldRaw.svelte
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import SelectField from './SelectField.svelte';
|
||||||
|
|
||||||
|
export let name;
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectField {...$$restProps} value={$values[name]} on:change={e => setFieldValue(name, e.target['value'])} />
|
||||||
10
packages/web/src/forms/FromTextFieldRaw.svelte
Normal file
10
packages/web/src/forms/FromTextFieldRaw.svelte
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let name;
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextField {...$$restProps} value={$values[name]} on:change={e => setFieldValue(name, e.target['value'])} />
|
||||||
14
packages/web/src/forms/SelectField.svelte
Normal file
14
packages/web/src/forms/SelectField.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let options = [];
|
||||||
|
export let value;
|
||||||
|
|
||||||
|
$: console.log('FIELD', $$props.value);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<select {...$$restProps} on:change>
|
||||||
|
{#each options as x (x.value)}
|
||||||
|
<option value={x.value} selected={value == x.value}>
|
||||||
|
{x.label}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
5
packages/web/src/forms/TextField.svelte
Normal file
5
packages/web/src/forms/TextField.svelte
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input type="text" {...$$restProps} bind:value on:change />
|
||||||
@@ -3,18 +3,42 @@
|
|||||||
import FormButton from '../forms/FormButton.svelte';
|
import FormButton from '../forms/FormButton.svelte';
|
||||||
import FormProvider from '../forms/FormProvider.svelte';
|
import FormProvider from '../forms/FormProvider.svelte';
|
||||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||||
|
import TabControl from '../widgets/TabControl.svelte';
|
||||||
|
import ConnectionModalDriverFields from './ConnectionModalDriverFields.svelte';
|
||||||
|
import FormFieldTemplateLarge from './FormFieldTemplateLarge.svelte';
|
||||||
|
|
||||||
import ModalBase from './ModalBase.svelte';
|
import ModalBase from './ModalBase.svelte';
|
||||||
|
|
||||||
|
export let connection;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProvider>
|
<FormProvider
|
||||||
<ModalBase {...$$restProps}>
|
template={FormFieldTemplateLarge}
|
||||||
|
initialValues={connection || { server: 'localhost', engine: 'mssql@dbgate-plugin-mssql' }}
|
||||||
|
>
|
||||||
|
<ModalBase {...$$restProps} noPadding>
|
||||||
<div slot="header">Add connection</div>
|
<div slot="header">Add connection</div>
|
||||||
xxx
|
|
||||||
|
<TabControl
|
||||||
|
isInline
|
||||||
|
tabs={[
|
||||||
|
{
|
||||||
|
label: 'Main',
|
||||||
|
component: ConnectionModalDriverFields,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SSH Tunnel',
|
||||||
|
slot: 1,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div slot="1">SSH</div>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
<div slot="footer" class="flex">
|
<div slot="footer" class="flex">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<FormButton value="Test" />
|
<FormButton value="Test" />
|
||||||
<FormSubmit value="Save" on:click={() => console.log('SAVE')} />
|
<FormSubmit value="Save" on:click={v => console.log('SAVE', v.detail)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalBase>
|
</ModalBase>
|
||||||
|
|||||||
55
packages/web/src/modals/ConnectionModalDriverFields.svelte
Normal file
55
packages/web/src/modals/ConnectionModalDriverFields.svelte
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
|
||||||
|
import FormTextField from '../forms/FormTextField.svelte';
|
||||||
|
import { extensions } from '../stores';
|
||||||
|
import { useAuthTypes } from '../utility/metadataLoaders';
|
||||||
|
|
||||||
|
const { values } = getFormContext();
|
||||||
|
$: authType = $values.authType;
|
||||||
|
$: engine = $values.engine;
|
||||||
|
$: authTypes = useAuthTypes({ engine });
|
||||||
|
$: currentAuthType = $authTypes && $authTypes.find(x => x.name == authType);
|
||||||
|
$: disabledFields = (currentAuthType ? currentAuthType.disabledFields : null) || [];
|
||||||
|
$: driver = $extensions.drivers.find(x => x.engine == engine);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormSelectField
|
||||||
|
label="Database engine"
|
||||||
|
name="engine"
|
||||||
|
options={[
|
||||||
|
{ label: '(select driver)', value: '' },
|
||||||
|
...$extensions.drivers.map(driver => ({
|
||||||
|
value: driver.engine,
|
||||||
|
label: driver.title,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-9 mr-1">
|
||||||
|
<FormTextField
|
||||||
|
label="Server"
|
||||||
|
name="server"
|
||||||
|
disabled={disabledFields.includes('server')}
|
||||||
|
templateProps={{ noMargin: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 mr-1">
|
||||||
|
<FormTextField
|
||||||
|
label="Port"
|
||||||
|
name="port"
|
||||||
|
disabled={disabledFields.includes('port')}
|
||||||
|
templateProps={{ noMargin: true }}
|
||||||
|
placeholder={driver && driver.defaultPort}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.row {
|
||||||
|
margin: var(--dim-large-form-margin);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
packages/web/src/modals/FormFieldTemplateLarge.svelte
Normal file
32
packages/web/src/modals/FormFieldTemplateLarge.svelte
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let type;
|
||||||
|
export let label;
|
||||||
|
export let noMargin;
|
||||||
|
export let disabled = false;
|
||||||
|
export let labelProps: any = {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="largeFormMarker" class:noMargin>
|
||||||
|
{#if type == 'checkbox'}
|
||||||
|
<slot />
|
||||||
|
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
|
||||||
|
{:else}
|
||||||
|
<div class="label" {...labelProps} on:click={labelProps.onClick}>
|
||||||
|
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.label {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
.largeFormMarker:not(.noMargin) {
|
||||||
|
margin: var(--dim-large-form-margin);
|
||||||
|
}
|
||||||
|
.disabled {
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
import clickOutside from '../utility/clickOutside';
|
import clickOutside from '../utility/clickOutside';
|
||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
|
|
||||||
export let fullScreen;
|
export let fullScreen = false;
|
||||||
export let noPadding;
|
export let noPadding = false;
|
||||||
export let modalId;
|
export let modalId;
|
||||||
|
|
||||||
function handleCloseModal() {
|
function handleCloseModal() {
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ const allFilesLoader = () => ({
|
|||||||
params: {},
|
params: {},
|
||||||
reloadTrigger: `all-files-changed`,
|
reloadTrigger: `all-files-changed`,
|
||||||
});
|
});
|
||||||
|
const authTypesLoader = ({ engine }) => ({
|
||||||
|
url: 'plugins/auth-types',
|
||||||
|
params: { engine },
|
||||||
|
reloadTrigger: `installed-plugins-changed`,
|
||||||
|
});
|
||||||
|
|
||||||
async function getCore(loader, args) {
|
async function getCore(loader, args) {
|
||||||
const { url, params, reloadTrigger, transform } = loader(args);
|
const { url, params, reloadTrigger, transform } = loader(args);
|
||||||
@@ -377,3 +382,10 @@ export function getFavorites(args) {
|
|||||||
export function useFavorites(args) {
|
export function useFavorites(args) {
|
||||||
return useCore(favoritesLoader, args);
|
return useCore(favoritesLoader, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAuthTypes(args) {
|
||||||
|
return getCore(authTypesLoader, args);
|
||||||
|
}
|
||||||
|
export function useAuthTypes(args) {
|
||||||
|
return useCore(authTypesLoader, args);
|
||||||
|
}
|
||||||
|
|||||||
93
packages/web/src/widgets/TabControl.svelte
Normal file
93
packages/web/src/widgets/TabControl.svelte
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
interface TabDef {
|
||||||
|
label: string;
|
||||||
|
slot?: number;
|
||||||
|
component?: any;
|
||||||
|
props?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let tabs: TabDef[];
|
||||||
|
export let value = 0;
|
||||||
|
export let isInline = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="main">
|
||||||
|
<div class="tabs">
|
||||||
|
{#each tabs as tab, index}
|
||||||
|
<div class="tab-item" class:selected={value == index} on:click={() => (value = index)}>
|
||||||
|
<span class="ml-2">
|
||||||
|
{tab.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-container">
|
||||||
|
{#each tabs as tab, index}
|
||||||
|
<div class="container" class:isInline class:tabVisible={index == value}>
|
||||||
|
<svelte:component this={tab.component} {...tab.props} />
|
||||||
|
{#if tab.slot == 0}<slot name="0" />{/if}
|
||||||
|
{#if tab.slot == 1}<slot name="1" />{/if}
|
||||||
|
{#if tab.slot == 2}<slot name="2" />{/if}
|
||||||
|
{#if tab.slot == 3}<slot name="3" />{/if}
|
||||||
|
{#if tab.slot == 4}<slot name="4" />{/if}
|
||||||
|
{#if tab.slot == 5}<slot name="5" />{/if}
|
||||||
|
{#if tab.slot == 6}<slot name="6" />{/if}
|
||||||
|
{#if tab.slot == 7}<slot name="7" />{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
height: var(--dim-tabs-height);
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--theme-bg-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
border-right: 1px solid var(--theme-border);
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .tab-item:hover {
|
||||||
|
color: ${props => props.theme.tabs_font_hover};
|
||||||
|
} */
|
||||||
|
.tab-item.selected {
|
||||||
|
background-color: var(--theme-bg-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container:not(.isInline) {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container:not(.tabVisible):not(.isInline) {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.isInline:not(.tabVisible) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user