From 0e41023889fb2f26ef5dc2a736dd59b819d92bf1 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Mon, 27 Jan 2025 09:47:58 -0500 Subject: [PATCH] feat: app builder accordion list and accordion tab component (#5132) * feat: tabs component accordion * accordion tabs * accordionlistcomponent * types * Update AppTabs.svelte --------- Co-authored-by: Ruben Fiszel --- .../display/AppAccordionList.svelte | 163 ++++++++++++++++++ .../apps/components/layout/AppTabs.svelte | 92 +++++++--- .../apps/editor/component/Component.svelte | 10 ++ .../apps/editor/component/components.ts | 40 ++++- .../components/apps/editor/component/sets.ts | 3 +- .../apps/editor/componentsPanel/cssUtils.ts | 7 + .../componentsPanel/quickStyleProperties.ts | 3 + 7 files changed, 291 insertions(+), 27 deletions(-) create mode 100644 frontend/src/lib/components/apps/components/display/AppAccordionList.svelte diff --git a/frontend/src/lib/components/apps/components/display/AppAccordionList.svelte b/frontend/src/lib/components/apps/components/display/AppAccordionList.svelte new file mode 100644 index 0000000000..5aa12072de --- /dev/null +++ b/frontend/src/lib/components/apps/components/display/AppAccordionList.svelte @@ -0,0 +1,163 @@ + + +{#each Object.keys(css ?? {}) as key (key)} + +{/each} + + + + +
+ {#if $app.subgrids?.[`${id}-0`]} + {#if Array.isArray(result) && result.length > 0} + {#each result ?? [] as value, index} +
+ + {#if activeIndex === index} +
+ { + 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} + > + { + if (!$connectingInput.opened) { + $selectedComponent = [id] + } + onFocus() + }} + /> + +
+ {/if} +
+ {/each} + {:else} + + + + {#if !Array.isArray(result)} +
Input data is not an array
+ {/if} + {/if} + {/if} +
+
diff --git a/frontend/src/lib/components/apps/components/layout/AppTabs.svelte b/frontend/src/lib/components/apps/components/layout/AppTabs.svelte index 4d702c5eef..ff85e7f87c 100644 --- a/frontend/src/lib/components/apps/components/layout/AppTabs.svelte +++ b/frontend/src/lib/components/apps/components/layout/AppTabs.svelte @@ -48,6 +48,8 @@ selectedTabIndex: 0 }) + const titleBarHeight = 40; // Example height of a single accordion title bar in pixels + function handleTabSelection() { selectedIndex = tabs?.indexOf(selected) outputs?.selectedTabIndex.set(selectedIndex) @@ -170,28 +172,72 @@ {/each} - {/if} - -
- {#if $app.subgrids} - {#each tabs ?? [] as _res, i} - { - if (!$connectingInput.opened) { - $selectedComponent = [id] - handleTabSelection() - } - }} - /> + {:else if resolvedConfig.tabsKind == 'accordion'} +
+ {#each tabs ?? [] as res, index} +
+ + {#if selected == res} +
+ { + if (!$connectingInput.opened) { + $selectedComponent = [id] + handleTabSelection() + } + }} + /> +
+ {/if} +
{/each} - {/if} -
+
+ {:else} +
+ {#if $app.subgrids} + {#each tabs ?? [] as _res, i} + { + if (!$connectingInput.opened) { + $selectedComponent = [id] + handleTabSelection() + } + }} + /> + {/each} + {/if} +
+ {/if} diff --git a/frontend/src/lib/components/apps/editor/component/Component.svelte b/frontend/src/lib/components/apps/editor/component/Component.svelte index 264da8082d..6d4d0a396e 100644 --- a/frontend/src/lib/components/apps/editor/component/Component.svelte +++ b/frontend/src/lib/components/apps/editor/component/Component.svelte @@ -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'} + {:else if component.type === 'statcomponent'} 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 @@ -446,7 +450,7 @@ const buttonColorOptions = [...BUTTON_COLORS] export const selectOptions = { buttonColorOptions, - tabsKindOptions: ['tabs', 'sidebar', 'invisibleOnView'], + tabsKindOptions: ['tabs', 'sidebar', 'accordion', 'invisibleOnView'], buttonSizeOptions: ['xs', 'sm', 'md', 'lg', 'xl'], tableSearchOptions: ['By Component', 'By Runnable', 'Disabled'], chartThemeOptions: ['theme1', 'theme2', 'theme3'], @@ -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, @@ -4152,6 +4175,17 @@ export const presetComponents = { }, type: 'sidebartabscomponent' }, + accordiontabcomponent: { + name: 'Accordion Tabs', + icon: ListCollapse, + targetComponent: 'tabscomponent' as const, + configuration: { + tabsKind: { + value: 'accordion' + } + }, + type: 'accordiontabcomponent' + }, invisibletabscomponent: { name: 'Invisible Tabs', icon: PanelTopInactive, diff --git a/frontend/src/lib/components/apps/editor/component/sets.ts b/frontend/src/lib/components/apps/editor/component/sets.ts index dc11ce9268..999db0b63a 100644 --- a/frontend/src/lib/components/apps/editor/component/sets.ts +++ b/frontend/src/lib/components/apps/editor/component/sets.ts @@ -3,7 +3,7 @@ import type { ComponentSet } from '../../types' const tabs: ComponentSet = { title: 'Tabs', components: ['tabscomponent', 'conditionalwrapper'], - presets: ['sidebartabscomponent', 'invisibletabscomponent'] + presets: ['sidebartabscomponent', 'accordiontabcomponent', 'invisibletabscomponent'] } as const const layout: ComponentSet = { @@ -19,6 +19,7 @@ const layout: ComponentSet = { 'modalcomponent', 'steppercomponent', 'carousellistcomponent', + 'accordionlistcomponent', 'decisiontreecomponent', 'navbarcomponent', 'recomputeallcomponent' diff --git a/frontend/src/lib/components/apps/editor/componentsPanel/cssUtils.ts b/frontend/src/lib/components/apps/editor/componentsPanel/cssUtils.ts index 81b450fd80..1e3f152c7e 100644 --- a/frontend/src/lib/components/apps/editor/componentsPanel/cssUtils.ts +++ b/frontend/src/lib/components/apps/editor/componentsPanel/cssUtils.ts @@ -299,6 +299,13 @@ export const customisationByComponent: Customisation[] = [ ], variables: [] }, + { + components: ['accordionlistcomponent'], + selectors: [ + { selector: '.wm-accordion', comment: 'Accordion component', customCssKey: 'container' } + ], + variables: [] + }, { components: ['formcomponent'], selectors: [ diff --git a/frontend/src/lib/components/apps/editor/componentsPanel/quickStyleProperties.ts b/frontend/src/lib/components/apps/editor/componentsPanel/quickStyleProperties.ts index 847075de5a..5cf626b4cc 100644 --- a/frontend/src/lib/components/apps/editor/componentsPanel/quickStyleProperties.ts +++ b/frontend/src/lib/components/apps/editor/componentsPanel/quickStyleProperties.ts @@ -654,6 +654,9 @@ export const quickStyleProperties: Record< header: [...containerDefaultProps, typographyGrouping], container: containerDefaultProps }, + accordionlistcomponent: { + container: containerDefaultProps + }, carousellistcomponent: { container: containerDefaultProps },