mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 00:01:49 +00:00
feat(frontend): disable tabs (#1623)
* feat(frontend): disable tabs * feat(frontend): disable tabs * feat(frontend): remove console.log * feat(frontend): remove GridTabDisabled for Stepper component
This commit is contained in:
@@ -4,7 +4,12 @@
|
||||
import { initConfig, initOutput } from '../../editor/appUtils'
|
||||
import { components } from '../../editor/component'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import InitializeComponent from '../helpers/InitializeComponent.svelte'
|
||||
@@ -15,6 +20,7 @@
|
||||
export let tabs: string[]
|
||||
export let customCss: ComponentCustomCSS<'tabscomponent'> | undefined = undefined
|
||||
export let render: boolean
|
||||
export let disabledTabs: RichConfiguration[]
|
||||
|
||||
let resolvedConfig = initConfig(
|
||||
components['tabscomponent'].initialData.configuration,
|
||||
@@ -76,23 +82,30 @@
|
||||
$: selected != undefined && handleTabSelection()
|
||||
let selectedIndex = tabs?.indexOf(selected) ?? -1
|
||||
$: css = concatCustomCss($app.css?.tabscomponent, customCss)
|
||||
|
||||
let resolvedDisabledTabs: boolean[] = []
|
||||
</script>
|
||||
|
||||
<InputValue {id} input={configuration.tabsKind} bind:value={resolvedConfig.tabsKind} />
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
{#each disabledTabs ?? [] as disableTab, index}
|
||||
<InputValue {id} input={disableTab} bind:value={resolvedDisabledTabs[index]} />
|
||||
{/each}
|
||||
|
||||
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full' : 'w-full'}>
|
||||
{#if !resolvedConfig.tabsKind || resolvedConfig.tabsKind == 'tabs' || (resolvedConfig.tabsKind == 'invisibleOnView' && $mode == 'dnd')}
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<Tabs bind:selected class={css?.tabRow?.class} style={css?.tabRow?.style}>
|
||||
{#each tabs ?? [] as res}
|
||||
{#each tabs ?? [] as res, index}
|
||||
<Tab
|
||||
value={res}
|
||||
class={css?.allTabs?.class}
|
||||
style={css?.allTabs?.style}
|
||||
selectedClass={css?.selectedTab?.class}
|
||||
selectedStyle={css?.selectedTab?.style}
|
||||
disabled={resolvedDisabledTabs[index]}
|
||||
>
|
||||
<span class="font-semibold">{res}</span>
|
||||
</Tab>
|
||||
|
||||
@@ -421,6 +421,7 @@
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
disabledTabs={component.disabledTabs}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
|
||||
@@ -106,6 +106,7 @@ export type VerticalDividerComponent = BaseComponent<'verticaldividercomponent'>
|
||||
export type FileInputComponent = BaseComponent<'fileinputcomponent'>
|
||||
export type TabsComponent = BaseComponent<'tabscomponent'> & {
|
||||
tabs: string[]
|
||||
disabledTabs: RichConfiguration[]
|
||||
}
|
||||
export type ContainerComponent = BaseComponent<'containercomponent'>
|
||||
export type DrawerComponent = BaseComponent<'drawercomponent'>
|
||||
|
||||
@@ -245,7 +245,9 @@
|
||||
{#if componentSettings.item.data.type === 'tabscomponent'}
|
||||
<GridTab
|
||||
bind:tabs={componentSettings.item.data.tabs}
|
||||
bind:disabledTabs={componentSettings.item.data.disabledTabs}
|
||||
bind:component={componentSettings.item.data}
|
||||
canDisableTabs
|
||||
/>
|
||||
{:else if componentSettings.item.data.type === 'steppercomponent'}
|
||||
<GridTab
|
||||
|
||||
@@ -3,15 +3,29 @@
|
||||
import CloseButton from '$lib/components/common/CloseButton.svelte'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext, tick } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import type { AppViewerContext, RichConfiguration } from '../../types'
|
||||
import { deleteGridItem } from '../appUtils'
|
||||
import type { AppComponent } from '../component'
|
||||
import PanelSection from './common/PanelSection.svelte'
|
||||
import { dndzone } from 'svelte-dnd-action'
|
||||
import { generateRandomString } from '$lib/utils'
|
||||
import { GripVertical } from 'lucide-svelte'
|
||||
import GridTabDisabled from './GridTabDisabled.svelte'
|
||||
|
||||
export let tabs: string[] = []
|
||||
export let disabledTabs: RichConfiguration[] = []
|
||||
|
||||
export let canDisableTabs: boolean = false
|
||||
|
||||
// Migration code
|
||||
$: if (tabs.length !== disabledTabs?.length && canDisableTabs) {
|
||||
disabledTabs = Array(tabs.length).fill({
|
||||
type: 'static',
|
||||
value: false,
|
||||
fieldType: 'boolean'
|
||||
})
|
||||
}
|
||||
|
||||
export let word: string = 'Tab'
|
||||
|
||||
export let component: AppComponent
|
||||
@@ -42,6 +56,8 @@
|
||||
}
|
||||
]
|
||||
component.numberOfSubgrids = items.length
|
||||
|
||||
disabledTabs = [...disabledTabs, { type: 'static', value: false, fieldType: 'boolean' }]
|
||||
}
|
||||
|
||||
function deleteSubgrid(index: number) {
|
||||
@@ -61,12 +77,16 @@
|
||||
// Remove the corresponding item from the items array
|
||||
items = items.filter((item) => item.originalIndex !== index)
|
||||
|
||||
// Delete the item in the disabledTabs array
|
||||
disabledTabs = disabledTabs.filter((_, i) => i !== index)
|
||||
|
||||
component.numberOfSubgrids = items.length
|
||||
// Update the originalIndex of the remaining items
|
||||
items.forEach((item, i) => {
|
||||
item.originalIndex = i
|
||||
})
|
||||
items = items
|
||||
|
||||
delete $app!.subgrids![items.length]
|
||||
$app = $app
|
||||
}
|
||||
@@ -96,6 +116,12 @@
|
||||
$app!.subgrids![`${component.id}-${items[i].originalIndex}`] ?? []
|
||||
}
|
||||
|
||||
const newDisabledTabs: RichConfiguration[] = []
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
newDisabledTabs.push(disabledTabs[items[i].originalIndex])
|
||||
}
|
||||
disabledTabs = newDisabledTabs
|
||||
|
||||
// update originalIndex
|
||||
items.forEach((item, i) => {
|
||||
item.originalIndex = i
|
||||
@@ -145,26 +171,34 @@
|
||||
on:finalize={handleFinalize}
|
||||
>
|
||||
{#each items as item, index (item.id)}
|
||||
<div class="w-full flex flex-row gap-2 items-center relative my-1">
|
||||
<input
|
||||
on:keydown|stopPropagation
|
||||
on:input={(e) => updateItemValue(index, e)}
|
||||
type="text"
|
||||
bind:value={items[index].value}
|
||||
/>
|
||||
<div class="absolute right-6">
|
||||
<CloseButton noBg on:close={() => deleteSubgrid(index)} />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div
|
||||
tabindex={dragDisabled ? 0 : -1}
|
||||
class="w-4 h-4"
|
||||
on:mousedown={startDrag}
|
||||
on:touchstart={startDrag}
|
||||
on:keydown={handleKeyDown}
|
||||
>
|
||||
<GripVertical size={16} />
|
||||
<div class="border rounded-md p-2 mb-2 bg-white">
|
||||
<div class="w-full flex flex-row gap-2 items-center relative my-1">
|
||||
<input
|
||||
on:keydown|stopPropagation
|
||||
on:input={(e) => updateItemValue(index, e)}
|
||||
type="text"
|
||||
bind:value={items[index].value}
|
||||
/>
|
||||
<div class="absolute right-6">
|
||||
<CloseButton noBg on:close={() => deleteSubgrid(index)} />
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
|
||||
<div
|
||||
tabindex={dragDisabled ? 0 : -1}
|
||||
class="w-4 h-4"
|
||||
on:mousedown={startDrag}
|
||||
on:touchstart={startDrag}
|
||||
on:keydown={handleKeyDown}
|
||||
>
|
||||
<GripVertical size={16} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if canDisableTabs}
|
||||
<GridTabDisabled bind:field={disabledTabs[index]} id={component.id} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import type { RichConfiguration } from '../../types'
|
||||
import InputsSpecEditor from './InputsSpecEditor.svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
export let id: string
|
||||
export let field: RichConfiguration
|
||||
|
||||
let disablable = !(field.type === 'static' && field.value === false)
|
||||
</script>
|
||||
|
||||
<Toggle
|
||||
bind:checked={disablable}
|
||||
size="xs"
|
||||
options={{
|
||||
right: 'Can be disabled'
|
||||
}}
|
||||
on:change={() => {
|
||||
if (disablable) {
|
||||
field = {
|
||||
type: 'eval',
|
||||
expr: 'false',
|
||||
fieldType: 'boolean'
|
||||
}
|
||||
} else {
|
||||
field = {
|
||||
type: 'static',
|
||||
value: false,
|
||||
fieldType: 'boolean'
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if disablable}
|
||||
<div transition:slide|local>
|
||||
<InputsSpecEditor
|
||||
key={`Tab disabled`}
|
||||
bind:componentInput={field}
|
||||
{id}
|
||||
userInputEnabled={false}
|
||||
shouldCapitalize={true}
|
||||
resourceOnly={false}
|
||||
hasRows={false}
|
||||
fieldType={field?.['fieldType']}
|
||||
subFieldType={field?.['subFieldType']}
|
||||
format={field?.['format']}
|
||||
selectOptions={field?.['selectOptions']}
|
||||
tooltip={field?.['tooltip']}
|
||||
onlyStatic={field?.['onlyStatic']}
|
||||
fileUpload={field?.['fileUpload']}
|
||||
placeholder={field?.['placeholder']}
|
||||
customTitle={field?.['customTitle']}
|
||||
displayType={false}
|
||||
noVariablePicker={true}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -11,6 +11,8 @@
|
||||
export let selectedClass = ''
|
||||
export let selectedStyle = ''
|
||||
|
||||
export let disabled: boolean = false
|
||||
|
||||
const fontSizeClasses = {
|
||||
xs: 'text-xs',
|
||||
sm: 'text-sm',
|
||||
@@ -30,7 +32,8 @@
|
||||
: 'border-gray-300 border-opacity-0 hover:border-opacity-100 text-gray-600',
|
||||
fontSizeClasses[size],
|
||||
c,
|
||||
$selected?.startsWith(value) ? selectedClass : ''
|
||||
$selected?.startsWith(value) ? selectedClass : '',
|
||||
disabled ? 'cursor-not-allowed text-gray-400' : ''
|
||||
)}
|
||||
style={`${style} ${$selected?.startsWith(value) ? selectedStyle : ''}`}
|
||||
on:click={() => {
|
||||
@@ -41,6 +44,7 @@
|
||||
}
|
||||
}}
|
||||
on:pointerdown|stopPropagation
|
||||
{disabled}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user