+
+
+## Returns
+
+- This component doesn't return a value. It's used for layout organization only.
+
+## Usage Example
+
+```python
+from preswald import collapsible, slider, text
+
+# Create a collapsible section for advanced filters
+collapsible("Advanced Filters", open=False)
+
+# All components below will be nested in the collapsible container
+text("Adjust the parameters below to filter the data.")
+slider("Sepal Width", min_val=0, max_val=10, default=5.5)
+slider("Sepal Length", min_val=0, max_val=10, default=4.5)
+
+# Create another section that's open by default
+collapsible("Main Visualizations")
+text("These are the primary visualizations for your data.")
+# Add more components here...
+```
+
+### Key Features
+
+1. **Organized Layout**: Group related components to create a cleaner, more structured interface.
+2. **Reduced Visual Clutter**: Hide optional or advanced controls that aren't needed immediately.
+3. **Improved User Experience**: Create step-by-step workflows or categorize UI elements by function.
+4. **Responsive Design**: Helps make dense dashboards more manageable on smaller screens.
+
+### Why Use `collapsible`?
+
+The `collapsible` component is essential for building complex applications with many UI elements. By organizing components into expandable/collapsible sections, you can create more intuitive interfaces that guide users through your data application.
+
+Enhance your layout with the `collapsible` component! 🔽
\ No newline at end of file
diff --git a/examples/iris/hello.py b/examples/iris/hello.py
index ca3ed935c..1ed590d30 100644
--- a/examples/iris/hello.py
+++ b/examples/iris/hello.py
@@ -5,6 +5,7 @@
from preswald import (
chat,
+ collapsible,
# fastplotlib,
get_df,
plotly,
@@ -29,6 +30,9 @@
# Load the CSV
df = get_df("iris_csv")
+# Add collapsible for Sepal visualizations
+collapsible("Sepal Visualizations", open=True)
+
# 1. Scatter plot - Sepal Length vs Sepal Width
text(
"## Sepal Length vs Sepal Width \n This scatter plot shows the relationship between sepal length and sepal width for different iris species. We can see that Setosa is well-separated from the other two species, while Versicolor and Virginica show some overlap."
@@ -68,6 +72,9 @@
fig5.update_layout(template="plotly_white")
plotly(fig5)
+# Add collapsible for Petal visualizations
+collapsible("Petal Visualizations", open=False)
+
# 4. Violin plot of Sepal Length by Species
text(
"## Sepal Length Distribution by Species \n The violin plot provides a better understanding of the distribution of sepal lengths within each species. We can see the density of values and how they vary across species."
@@ -96,68 +103,8 @@
fig10.update_layout(template="plotly_white")
plotly(fig10)
-# # 6. Fastplotlib Examples
-#
-# # Retrieve client_id from component state
-# client_id = service.get_component_state("client_id")
-#
-# sidebar(defaultopen=True)
-# text("# Fastplotlib Examples")
-#
-# # 6.1. Simple Image Plot
-# text("## Simple Image Plot")
-# fig = fpl.Figure(size=(700, 560), canvas="offscreen")
-# fig._client_id = client_id
-# fig._label = "Simple Image Plot"
-# data = iio.imread("images/logo.png")
-# fig[0, 0].add_image(data)
-# fastplotlib(fig)
-#
-# # 6.2. Line Plot
-# text("## Line Plot")
-# x = np.linspace(-1, 10, 100)
-# y = np.sin(x)
-# sine = np.column_stack([x, y])
-# fig = fpl.Figure(size=(700, 560), canvas="offscreen")
-# fig._client_id = client_id
-# fig._label = "Line Plot"
-# fig[0, 0].add_line(data=sine, colors="w")
-# fastplotlib(fig)
-#
-# # 6.3. Line Plot with Color Maps
-# text("## Line Plot ColorMap")
-# fig = fpl.Figure(size=(700, 560), canvas="offscreen")
-# fig._client_id = client_id
-# fig._label = "Line Plot Color Map"
-# xs = np.linspace(-10, 10, 100)
-# ys = np.sin(xs)
-# sine = np.dstack([xs, ys])[0]
-# ys = np.cos(xs) - 5
-# cosine = np.dstack([xs, ys])[0]
-#
-# sine_graphic = fig[0, 0].add_line(
-# data=sine, thickness=10, cmap="plasma", cmap_transform=sine[:, 1]
-# )
-# labels = [0] * 25 + [5] * 10 + [1] * 35 + [2] * 30
-# cosine_graphic = fig[0, 0].add_line(
-# data=cosine, thickness=10, cmap="tab10", cmap_transform=labels
-# )
-# fastplotlib(fig)
-#
-# # 6.4. Scatter Plot from Iris dataset
-# text("## Scatter Plot")
-# x = df["sepal.length"].tolist()
-# y = df["petal.width"].tolist()
-# variety = df["variety"].tolist()
-# data = np.column_stack((x, y))
-# color_map = {"Setosa": "yellow", "Versicolor": "cyan", "Virginica": "magenta"}
-# colors = [color_map[v] for v in variety]
-#
-# fig = fpl.Figure(size=(700, 560), canvas="offscreen")
-# fig._client_id = client_id
-# fig._label = "Scatter Plot"
-# fig[0, 0].add_scatter(data=data, sizes=4, colors=colors)
-# fastplotlib(fig)
+# Add collapsible for data view
+collapsible("Dataset View", open=False)
# Show the first 10 rows of the dataset
text(
diff --git a/frontend/components.json b/frontend/components.json
index d1b7544df..4da4ee898 100644
--- a/frontend/components.json
+++ b/frontend/components.json
@@ -18,4 +18,4 @@
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
-}
\ No newline at end of file
+}
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
index aa95a80d2..b9bcbcda4 100644
--- a/frontend/eslint.config.js
+++ b/frontend/eslint.config.js
@@ -1,24 +1,17 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import react from 'eslint-plugin-react'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import importPlugin from 'eslint-plugin-import'
-
+import js from '@eslint/js';
+import importPlugin from 'eslint-plugin-import';
+import react from 'eslint-plugin-react';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import globals from 'globals';
export default [
{
- ignores: [
- 'dist',
- 'node_modules',
- '*.config.js',
- ]
+ ignores: ['dist', 'node_modules', '*.config.js'],
},
{
files: ['**/*.{js,jsx}'],
- extends: [
- 'prettier'
- ],
+ extends: ['prettier'],
languageOptions: {
ecmaVersion: 2020,
globals: {
@@ -51,48 +44,45 @@ export default [
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
+ 'no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ },
],
- 'no-unused-vars': ['error', {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
// Disable ESLint's import ordering to let Prettier handle it
'import/order': 'off',
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
+ 'no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ },
],
- 'no-unused-vars': ['error', {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
- 'import/order': ['error', {
- groups: [
- 'builtin',
- 'external',
- 'internal',
- ['parent', 'sibling'],
- 'index',
- ],
- 'newlines-between': 'always',
- pathGroups: [
- { pattern: '^react', group: 'external', position: 'before' },
- { pattern: '^@/components/(.*)$', group: 'internal', position: 'before' },
- { pattern: '^@/(.*)$', group: 'internal' },
- ],
- alphabetize: {
- order: 'asc',
- caseInsensitive: true,
+ 'import/order': [
+ 'error',
+ {
+ groups: ['builtin', 'external', 'internal', ['parent', 'sibling'], 'index'],
+ 'newlines-between': 'always',
+ pathGroups: [
+ { pattern: '^react', group: 'external', position: 'before' },
+ { pattern: '^@/components/(.*)$', group: 'internal', position: 'before' },
+ { pattern: '^@/(.*)$', group: 'internal' },
+ ],
+ alphabetize: {
+ order: 'asc',
+ caseInsensitive: true,
+ },
},
- }],
+ ],
},
},
-]
\ No newline at end of file
+];
diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js
index 2e7af2b7f..2aa7205d4 100644
--- a/frontend/postcss.config.js
+++ b/frontend/postcss.config.js
@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
-}
+};
diff --git a/frontend/src/components/DynamicComponents.jsx b/frontend/src/components/DynamicComponents.jsx
index 13b59208a..e75d31158 100644
--- a/frontend/src/components/DynamicComponents.jsx
+++ b/frontend/src/components/DynamicComponents.jsx
@@ -14,6 +14,7 @@ import BigNumberWidget from './widgets/BigNumberWidget';
import ButtonWidget from './widgets/ButtonWidget';
import ChatWidget from './widgets/ChatWidget';
import CheckboxWidget from './widgets/CheckboxWidget';
+import CollapsibleWidget from './widgets/CollapsibleWidget';
import DAGVisualizationWidget from './widgets/DAGVisualizationWidget';
import DataVisualizationWidget from './widgets/DataVisualizationWidget';
import FastplotlibWidget from './widgets/FastplotlibWidget';
@@ -74,6 +75,18 @@ const MemoizedComponent = memo(
case 'sidebar':
return