accordionlistcomponent

This commit is contained in:
Alexander Petric
2025-01-24 16:46:21 -05:00
parent b01c9c69fc
commit 6ba3f0c566
7 changed files with 211 additions and 8 deletions
@@ -0,0 +1,159 @@
<script lang="ts">
import { getContext } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
import { initCss } from '../../utils'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
import type { AppInput } from '../../inputType'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import ListWrapper from '../layout/ListWrapper.svelte'
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let componentInput: AppInputWithValues | undefined
export let customCss: ComponentCustomCSS<'accordionlistcomponent'> | undefined = undefined
export let render: boolean
export let initializing: boolean | undefined
export let componentContainerHeight: number
type AppInputWithValues = {
value: { header: string }[]
} & AppInput
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
let activeIndex: number = 0
const outputs = initOutput($worldStore, id, {
result: undefined,
activeIndex: 0,
loading: false,
inputs: {}
})
function onFocus() {
$focusedGrid = {
parentComponentId: id,
subGridIndex: 0
}
}
let css = initCss($app.css?.accordionlistcomponent, customCss)
let result: any[] | undefined = undefined
let inputs = {}
$: $selectedComponent?.includes(id) &&
$focusedGrid === undefined &&
($focusedGrid = {
parentComponentId: id,
subGridIndex: 0
})
function toggleAccordion(index: number) {
activeIndex = activeIndex === index ? -1 : index
outputs.activeIndex.set(activeIndex)
}
</script>
{#each Object.keys(css ?? {}) as key (key)}
<ResolveStyle
{id}
{customCss}
{key}
bind:css={css[key]}
componentStyle={$app.css?.accordionlistcomponent}
/>
{/each}
<InitializeComponent {id} />
<RunnableWrapper
render={true}
{outputs}
autoRefresh
{componentInput}
{id}
bind:initializing
bind:result
>
<div class="w-full flex flex-col overflow-auto max-h-full">
{#if $app.subgrids?.[`${id}-0`]}
{#if Array.isArray(result) && result.length > 0}
{#each result ?? [] as value, index}
<div class="border-b">
<button
on:pointerdown|stopPropagation
on:click={() => toggleAccordion(index)}
class={twMerge(
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
'wm-tabs-alltabs',
activeIndex === index
? twMerge('bg-surface text-primary ', 'wm-tabs-selectedTab')
: 'text-secondary'
)}
>
<span class="mr-2 w-8 font-mono">{activeIndex === index ? '-' : '+'}</span>
{componentInput?.value[index].header || `Header ${index}`}
</button>
{#if activeIndex === index}
<div class="p-2 overflow-auto w-full">
<ListWrapper
onSet={(id, value) => {
if (!inputs[id]) {
inputs[id] = { [index]: value }
} else {
inputs[id] = { ...inputs[id], [index]: value }
}
outputs?.inputs.set(inputs, true)
}}
onRemove={(id) => {
if (inputs?.[id] == undefined) {
return
}
if (index == 0) {
delete inputs[id]
inputs = { ...inputs }
} else {
delete inputs[id][index]
inputs[id] = { ...inputs[id] }
}
outputs?.inputs.set(inputs, true)
}}
{value}
{index}
>
<SubGridEditor
{id}
visible={render}
class={twMerge(css?.container?.class, 'wm-accordion')}
style={css?.container?.style}
subGridId={`${id}-0`}
containerHeight={componentContainerHeight -
(30 * componentInput?.value.length + 40)}
on:focus={() => {
if (!$connectingInput.opened) {
$selectedComponent = [id]
}
onFocus()
}}
/>
</ListWrapper>
</div>
{/if}
</div>
{/each}
{:else}
<ListWrapper disabled value={undefined} index={0}>
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
</ListWrapper>
{#if !Array.isArray(result)}
<div class="text-center text-tertiary">Input data is not an array</div>
{/if}
{/if}
{/if}
</div>
</RunnableWrapper>
@@ -175,24 +175,24 @@
{:else if resolvedConfig.tabsKind == 'accordion'}
<div class="flex flex-col w-full">
{#each tabs ?? [] as res, index}
<div class="mb-2">
<div class="border-b">
<button
on:pointerdown|stopPropagation
on:click={() => (selected = res)}
class={twMerge(
'w-full text-left p-2 cursor-pointer flex items-center text-secondary',
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
css?.allTabs?.class,
'wm-tabs-alltabs',
selected == res
? twMerge(
'bg-surface text-primary',
'bg-surface text-primary ',
css?.selectedTab?.class,
'wm-tabs-selectedTab'
)
: ''
: 'text-secondary'
)}
>
<span class="mr-2">{selected == res ? '-' : '+'}</span>
<span class="mr-2 w-8 font-mono">{selected == res ? '-' : '+'}</span>
{res}
</button>
{#if selected == res}
@@ -27,6 +27,7 @@
import AppJobIdLogComponent from '../../components/display/AppJobIdLogComponent.svelte'
import AppJobIdFlowStatus from '../../components/display/AppJobIdFlowStatus.svelte'
import AppCarouselList from '../../components/display/AppCarouselList.svelte'
import AppAccordionList from '../../components/display/AppAccordionList.svelte'
import AppAggridTableEe from '../../components/display/table/AppAggridTableEe.svelte'
import AppCustomComponent from '../../components/display/AppCustomComponent.svelte'
import AppStatCard from '../../components/display/AppStatCard.svelte'
@@ -871,6 +872,15 @@
{render}
bind:initializing
/>
{:else if component.type === 'accordionlistcomponent'}
<AppAccordionList
id={component.id}
componentInput={component.componentInput}
customCss={component.customCss}
{componentContainerHeight}
{render}
bind:initializing
/>
{:else if component.type === 'statcomponent'}
<AppStatCard
id={component.id}
@@ -51,7 +51,9 @@ import {
CalendarClock,
AppWindow,
PanelTop,
RefreshCw
RefreshCw,
ListCollapse,
GalleryThumbnails
} from 'lucide-svelte'
import type {
Aligned,
@@ -244,6 +246,7 @@ export type SelectTabComponent = BaseComponent<'selecttabcomponent'>
export type SelectStepComponent = BaseComponent<'selectstepcomponent'>
export type CarouselListComponent = BaseComponent<'carousellistcomponent'>
export type AccordionListComponent = BaseComponent<'accordionlistcomponent'>
export type StatisticCardComponent = BaseComponent<'statcomponent'>
export type MenuComponent = BaseComponent<'menucomponent'> & {
menuItems: (BaseAppComponent & ButtonComponent & GridItem)[]
@@ -351,6 +354,7 @@ export type TypedComponent =
| DownloadComponent
| ChartJsComponent
| CarouselListComponent
| AccordionListComponent
| PlotlyComponentV2
| ChartJsComponentV2
| StatisticCardComponent
@@ -2852,7 +2856,7 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm'
},
carousellistcomponent: {
name: 'Carousel List',
icon: ListIcon,
icon: GalleryThumbnails,
documentationLink: `${documentationBaseUrl}/carousel`,
dims: '3:8-12:8' as AppComponentDimensions,
customCss: {
@@ -2878,6 +2882,25 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm'
numberOfSubgrids: 1
}
},
accordionlistcomponent: {
name: 'Accordion List',
icon: ListCollapse,
documentationLink: `${documentationBaseUrl}/accordion`,
dims: '3:8-12:8' as AppComponentDimensions,
customCss: {
container: { class: '', style: '' }
},
initialData: {
configuration: {},
componentInput: {
type: 'static',
fieldType: 'array',
subFieldType: 'object',
value: [{ header: 'First', foo: 1 }, { header: 'Second', foo: 2 }, { header: 'Third', foo: 3 }] as object[]
},
numberOfSubgrids: 1
}
},
iconcomponent: {
name: 'Icon',
icon: Smile,
@@ -4154,7 +4177,7 @@ export const presetComponents = {
},
accordiontabcomponent: {
name: 'Accordion Tabs',
icon: List,
icon: ListCollapse,
targetComponent: 'tabscomponent' as const,
configuration: {
tabsKind: {
@@ -19,6 +19,7 @@ const layout: ComponentSet = {
'modalcomponent',
'steppercomponent',
'carousellistcomponent',
'accordionlistcomponent',
'decisiontreecomponent',
'navbarcomponent',
'recomputeallcomponent'
@@ -299,6 +299,13 @@ export const customisationByComponent: Customisation[] = [
],
variables: []
},
{
components: ['accordionlistcomponent'],
selectors: [
{ selector: '.wm-accordion', comment: 'Accordion component', customCssKey: 'container' }
],
variables: []
},
{
components: ['formcomponent'],
selectors: [
@@ -654,6 +654,9 @@ export const quickStyleProperties: Record<
header: [...containerDefaultProps, typographyGrouping],
container: containerDefaultProps
},
accordionlistcomponent: {
container: containerDefaultProps
},
carousellistcomponent: {
container: containerDefaultProps
},