import dash
import dash_mantine_components as dmc
import numpy as np
from dash import MATCH, ALL, Dash, Input, Output, State, _dash_renderer, clientside_callback, dcc, html, no_update, callback
from dash_iconify import DashIconify
import pandas as pd
import uuid
_dash_renderer._set_react_version("18.2.0")
# Generate random data
np.random.seed(42) # for reproducibility
# Sample data for names and countries
first_names = ['James', 'Mary', 'John', 'Patricia', 'Robert', 'Jennifer', 'Michael', 'Linda', 'William', 'Elizabeth',
'David', 'Barbara', 'Richard', 'Susan', 'Joseph', 'Jessica', 'Thomas', 'Sarah', 'Charles', 'Karen']
last_names = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez',
'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson', 'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin']
countries = ['USA', 'Canada', 'UK', 'Australia', 'Germany', 'France', 'Japan', 'Brazil', 'India', 'China',
'Italy', 'Spain', 'Mexico', 'South Korea', 'Russia', 'Netherlands', 'Switzerland', 'Sweden', 'Norway', 'Denmark']
# Generate 1000 random entries
n_entries = 1000
df = pd.DataFrame({
'first_name': np.random.choice(first_names, n_entries),
'last_name': np.random.choice(last_names, n_entries),
'country': np.random.choice(countries, n_entries),
'age': np.random.randint(18, 80, n_entries),
'salary': np.random.randint(30000, 150000, n_entries)
})
# Combine first and last names
df['full_name'] = df['first_name'] + ' ' + df['last_name']
df['id'] = [uuid.uuid4().hex for _ in range(n_entries)]
app = Dash(__name__)
app.layout = dmc.MantineProvider(
children=[
dmc.Container(
size='md',
p='md',
children=dmc.Stack(
children=[
dmc.Title(
'Clientside searchable table with pagination',
order=3,
),
dmc.Group(
justify='left',
children=[
dmc.TextInput(
id='search',
placeholder='Enter any text to search',
label='Search',
),
],
),
dmc.Container(
fluid=True,
pos="relative",
children=[
dmc.LoadingOverlay(
visible=False,
id="loading-overlay",
overlayProps={"radius": "sm", "blur": 2},
zIndex=10,
),
html.Div(
id='container',
children=dmc.Container(
mx='md',
px=0,
fluid=True,
children=[
dmc.Skeleton(h=10, my="md"),
dmc.Skeleton(h=10, my="md"),
dmc.Skeleton(h=10, my="md"),
]
)
),
]
),
dmc.Group(
justify='right',
children=dmc.Pagination(
id='pagination',
value=1,
total=df.shape[0],
size='sm',
)
),
dcc.Store(
id='store-data',
data=df.to_dict(orient='records'),
),
dcc.Store(id="edit-btn-store", data={}),
dmc.Modal(
id='modal',
)
]
)
)
]
)
clientside_callback(
"""
function(search, page, data) {
const dmc = window.dash_mantine_components;
const dash_iconify = window.dash_iconify;
if (search && search.length > 0) {
const searchUpper = search.toUpperCase();
data = data.filter(row => {
return Object.values(row).some(value => {
return String(value).toUpperCase().includes(searchUpper);
});
});
}
const length = 100;
page = page || 1;
const startIdx = (page - 1) * length;
const endIdx = Math.min(startIdx + length, data.length);
const pageData = data.slice(startIdx, endIdx);
const header = React.createElement(dmc.TableThead, {
children: React.createElement(dmc.TableTr, {
children: [
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'First Name',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Last Name',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Country',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Age',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Salary',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Full Name',
size: 'sm',
fw: 'bold'
})
}),
React.createElement(dmc.TableTh, {
children: React.createElement(dmc.Text, {
children: 'Edit',
size: 'sm',
fw: 'bold'
})
}),
]
})
});
const rows = pageData.map(row => {
const rowCopy = {...row};
const rowId = dash_component_api.stringifyId({
index: row.id,
type: 'edit-btn'
});
const cells = [
React.createElement(dmc.Text, {children: rowCopy.first_name, size: 'sm'}),
React.createElement(dmc.Text, {children: rowCopy.last_name, size: 'sm'}),
React.createElement(dmc.Text, {children: rowCopy.country, size: 'sm'}),
React.createElement(dmc.Text, {children: rowCopy.age, size: 'sm'}),
React.createElement(dmc.Text, {children: rowCopy.salary, size: 'sm'}),
React.createElement(dmc.Text, {children: rowCopy.full_name, size: 'sm'}),
React.createElement(dmc.ActionIcon, {
id: rowId,
n_clicks: 0,
variant: 'transparent',
onClick: () => {dash_clientside.set_props('edit-btn-store', {data: new Date().toISOString()})},
children: React.createElement(dash_iconify.DashIconify, {
icon: 'mdi:pencil',
height: 16
})
})
]
return React.createElement(
dmc.TableTr,
{},
cells.map((cell, index) => {
return React.createElement(
dmc.TableTd,
{
children: cell,
},
);
})
);
});
dash_clientside.set_props('pagination', {
total: Math.ceil(data.length / 100),
});
const table = React.createElement(dmc.Table, {
striped: true,
withRowBorders: true,
withColumnBorders: false,
highlightOnHover: false,
stickyHeader: true,
}, [header, rows]);
const scrollArea = React.createElement(dmc.ScrollArea, {
h: '800px',
children: table,
});
return [
scrollArea,
];
}
""",
Output('container', 'children'),
Input('search', 'value'),
Input('pagination', 'value'),
State('store-data', 'data'),
running=(Output('loading-overlay', 'visible'), True, False),
prevent_initial_call=False,
)
@callback(
Output('modal', 'opened'),
Input('edit-btn-store', 'data'),
prevent_initial_call=True,
)
def func(click_data):
if not any(click_data):
return False
print('entered callback')
return True
if __name__ == "__main__":
app.run(debug=True)