├── .github
├── FUNDING.yml
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── .vscode
└── launch.json
├── LICENSE
├── MANIFEST.in
├── Pipfile
├── README.md
├── SECURITY.md
├── awesome_table
├── __init__.py
├── column
│ └── __init__.py
└── frontend
│ ├── .prettierrc
│ ├── build
│ ├── asset-manifest.json
│ ├── bootstrap.min.css
│ ├── index.html
│ ├── precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js
│ ├── service-worker.js
│ └── static
│ │ ├── css
│ │ ├── 2.2515e1bc.chunk.css
│ │ └── 2.2515e1bc.chunk.css.map
│ │ ├── js
│ │ ├── 2.0faa2557.chunk.js
│ │ ├── 2.0faa2557.chunk.js.LICENSE.txt
│ │ ├── 2.0faa2557.chunk.js.map
│ │ ├── main.92daa278.chunk.js
│ │ ├── main.92daa278.chunk.js.map
│ │ ├── runtime-main.11ec9aca.js
│ │ └── runtime-main.11ec9aca.js.map
│ │ └── media
│ │ ├── fa-brands-400.ee91e640.woff2
│ │ ├── fa-brands-400.f34b6a2a.ttf
│ │ ├── fa-regular-400.65e80529.ttf
│ │ ├── fa-regular-400.82bafee9.woff2
│ │ ├── fa-solid-900.52afeb7a.ttf
│ │ ├── fa-solid-900.57b380d2.woff2
│ │ ├── fa-v4compatibility.43044320.woff2
│ │ └── fa-v4compatibility.9d6f359a.ttf
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── bootstrap.min.css
│ └── index.html
│ ├── src
│ ├── AwesomeTable.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ └── tsconfig.json
├── build
└── lib
│ └── awesome_table
│ ├── __init__.py
│ ├── column
│ └── __init__.py
│ └── frontend
│ └── build
│ ├── asset-manifest.json
│ ├── bootstrap.min.css
│ ├── index.html
│ ├── precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js
│ ├── service-worker.js
│ └── static
│ ├── css
│ ├── 2.2515e1bc.chunk.css
│ └── 2.2515e1bc.chunk.css.map
│ ├── js
│ ├── 2.0faa2557.chunk.js
│ ├── 2.0faa2557.chunk.js.LICENSE.txt
│ ├── 2.0faa2557.chunk.js.map
│ ├── main.92daa278.chunk.js
│ ├── main.92daa278.chunk.js.map
│ ├── runtime-main.11ec9aca.js
│ └── runtime-main.11ec9aca.js.map
│ └── media
│ ├── fa-brands-400.ee91e640.woff2
│ ├── fa-brands-400.f34b6a2a.ttf
│ ├── fa-regular-400.65e80529.ttf
│ ├── fa-regular-400.82bafee9.woff2
│ ├── fa-solid-900.52afeb7a.ttf
│ ├── fa-solid-900.57b380d2.woff2
│ ├── fa-v4compatibility.43044320.woff2
│ └── fa-v4compatibility.9d6f359a.ttf
├── samples
├── simple_table
│ ├── __init__.py
│ ├── awesome-table-simple.png
│ └── sample.py
├── with_columns
│ ├── __init__.py
│ ├── awesome-table-with-columns.png
│ └── sample.py
├── with_iconbutton
│ ├── __init__.py
│ ├── awesome-table-with-iconbutton.png
│ └── sample.py
├── with_image
│ ├── __init__.py
│ ├── awesome-table-with-image.png
│ └── sample.py
├── with_order
│ ├── __init__.py
│ ├── awesome-table-with-order.png
│ └── sample.py
├── with_search
│ ├── __init__.py
│ ├── awesome-table-with-search.png
│ └── sample.py
└── with_sidebar
│ ├── __init__.py
│ ├── awesome-table-with-sidebar.png
│ └── sample.py
├── setup.cfg
└── setup.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [caiodearaujo]
4 | patreon: caiofaar
5 | open_collective: caiofaar
6 | ko_fi: caiofaar
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '22 0 * * 1'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript', 'python' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | **/__pycache__
10 | *.egg-info/
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 |
81 | # Next.js build output
82 | .next
83 |
84 | # Nuxt.js build / generate output
85 | .nuxt
86 | dist
87 |
88 | # Gatsby files
89 | .cache/
90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
91 | # https://nextjs.org/blog/next-9-1#public-directory-support
92 | # public
93 |
94 | # vuepress build output
95 | .vuepress/dist
96 |
97 | # Serverless directories
98 | .serverless/
99 |
100 | # FuseBox cache
101 | .fusebox/
102 |
103 | # DynamoDB Local files
104 | .dynamodb/
105 |
106 | # TernJS port file
107 | .tern-port
108 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use o IntelliSense para saber mais sobre os atributos possíveis.
3 | // Focalizar para exibir as descrições dos atributos existentes.
4 | // Para obter mais informações, acesse: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Python: Módulo",
9 | "type": "python",
10 | "request": "launch",
11 | "module": "streamlit",
12 | "args": [
13 | "run",
14 | "./awesome_table/__init__.py"
15 | ],
16 | "justMyCode": false,
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018-2021 Streamlit Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include awesome_table/frontend/build *
2 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 |
8 | [dev-packages]
9 |
10 | [requires]
11 | python_version = "3.9"
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Awesome Table for Streamlit
2 |
3 | ---
4 |
5 | Awesome table is a component to use with [Streamlit](https://github.com/streamlit/streamlit) with order and search components.
6 |
7 | ## Installing AwesomeTable using PiP
8 | ```
9 | pip install streamlit-awesome-table
10 | ```
11 |
12 | ## Examples
13 | ### **Create a simple table**
14 | [View complete source here](/samples/simple_table/__init__.py)
15 |
16 | ```
17 | import pandas as pd
18 | from awesome_table import AwesomeTable
19 |
20 | sample_data = {...}
21 |
22 | AwesomeTable(pd.json_normalize(sample_data))
23 | ```
24 | 
25 |
26 | ### **Create a table with columns**
27 | [View complete source here](/samples/with_columns/__init__.py)
28 |
29 | ```
30 | import pandas as pd
31 | from awesome_table import AwesomeTable
32 | from awesome_table.column import Column
33 |
34 | sample_data = {...}
35 |
36 | AwesomeTable(pd.json_normalize(sample_data), columns=[
37 | Column(name='id', label='ID'),
38 | Column(name='name', label='Name'),
39 | Column(name='job_title', label='Job Title'),
40 | Column(name='avatar', label='Avatar'),
41 | Column(name='_url.social_media', label='Social Media'),
42 | Column(name='_url.document', label='Document'),
43 | ])
44 | ```
45 | 
46 |
47 | ### **Create a table with icon button**
48 | [View complete source here](/samples/with_iconbutton/__init__.py)
49 |
50 | ```
51 | import pandas as pd
52 | from awesome_table import AwesomeTable
53 | from awesome_table.column import (Column, ColumnDType)
54 |
55 | sample_data = {...}
56 |
57 | AwesomeTable(pd.json_normalize(sample_data), columns=[
58 | Column(name='id', label='ID'),
59 | Column(name='name', label='Name'),
60 | Column(name='job_title', label='Job Title'),
61 | Column(name='avatar', label='Avatar'),
62 | Column(name='_url.social_media', label='Social Media', dtype=ColumnDType.ICONBUTTON, icon='fa-solid fa-share-nodes'), ## From FontAwesome v6.0.0
63 | Column(name='_url.document', label='Document', dtype=ColumnDType.DOWNLOAD),
64 | ])
65 | ```
66 | 
67 |
68 | ### **Create a table with order**
69 | [View complete source here](/samples/with_order/__init__.py)
70 |
71 | ```
72 | import pandas as pd
73 | from awesome_table import AwesomeTable
74 | from awesome_table.column import (Column, ColumnDType)
75 |
76 | sample_data = {...}
77 |
78 | AwesomeTable(pd.json_normalize(sample_data), columns=[
79 | Column(name='id', label='ID'),
80 | Column(name='name', label='Name'),
81 | Column(name='job_title', label='Job Title'),
82 | Column(name='avatar', label='Avatar'),
83 | Column(name='_url.social_media', label='Social Media', dtype=ColumnDType.ICONBUTTON, icon='fa-solid fa-share-nodes'), ## From FontAwesome v6.0.0
84 | Column(name='_url.document', label='Document', dtype=ColumnDType.DOWNLOAD),
85 | ], show_order=True)
86 | ```
87 | 
88 |
89 | ### **Create a table with search**
90 | [View complete source here](/samples/with_search/__init__.py)
91 |
92 | ```
93 | import pandas as pd
94 | from awesome_table import AwesomeTable
95 | from awesome_table.column import (Column, ColumnDType)
96 |
97 | sample_data = {...}
98 |
99 | AwesomeTable(pd.json_normalize(sample_data), columns=[
100 | Column(name='id', label='ID'),
101 | Column(name='name', label='Name'),
102 | Column(name='job_title', label='Job Title'),
103 | Column(name='avatar', label='Avatar'),
104 | Column(name='_url.social_media', label='Social Media', dtype=ColumnDType.ICONBUTTON, icon='fa-solid fa-share-nodes'), ## From FontAwesome v6.0.0
105 | Column(name='_url.document', label='Document', dtype=ColumnDType.DOWNLOAD),
106 | ], show_search=True)
107 | ```
108 | 
109 |
110 | ### **Create a table with sidebar**
111 | [View complete source here](/samples/with_sidebar/__init__.py)
112 |
113 | ```
114 | import pandas as pd
115 | from awesome_table import AwesomeTable
116 | from awesome_table.column import (Column, ColumnDType)
117 |
118 | sample_data = {...}
119 |
120 | AwesomeTable(pd.json_normalize(sample_data), columns=[
121 | Column(name='id', label='ID'),
122 | Column(name='name', label='Name'),
123 | Column(name='job_title', label='Job Title'),
124 | Column(name='avatar', label='Avatar'),
125 | Column(name='_url.social_media', label='Social Media', dtype=ColumnDType.ICONBUTTON, icon='fa-solid fa-share-nodes'), ## From FontAwesome v6.0.0
126 | Column(name='_url.document', label='Document', dtype=ColumnDType.DOWNLOAD),
127 | ], show_order=True, show_search=True, show_search_order_in_sidebar=True)
128 | ```
129 | 
130 |
131 | ### **Create a table with image**
132 | [View complete source here](/samples/with_image/__init__.py)
133 |
134 | ```
135 | import pandas as pd
136 | from awesome_table import AwesomeTable
137 | from awesome_table.column import (Column, ColumnDType)
138 |
139 | sample_data = {...}
140 |
141 | AwesomeTable(pd.json_normalize(sample_data), columns=[
142 | Column(name='id', label='ID'),
143 | Column(name='name', label='Name'),
144 | Column(name='job_title', label='Job Title'),
145 | Column(name='avatar', label='Avatar', dtype=ColumnDType.IMAGE),
146 | Column(name='_url.social_media', label='Social Media', dtype=ColumnDType.ICONBUTTON, icon='fa-solid fa-share-nodes'), ## From FontAwesome v6.0.0
147 | Column(name='_url.document', label='Document', dtype=ColumnDType.DOWNLOAD),
148 | ], show_search=True, show_order=True)
149 | ```
150 | 
151 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :white_check_mark: |
12 | | 4.0.x | :x: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/awesome_table/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | import streamlit as st
4 | from typing import List
5 | import streamlit.components.v1 as components
6 | from awesome_table.column import (ColumnDType, Column)
7 |
8 | _RELEASE = True
9 |
10 | class AwesomeTable():
11 | """AwesomeTable is a component for Streamlit to build a table based in bootstrap with search and order funcionality."""
12 | if _RELEASE:
13 | _root_dir = os.path.dirname(os.path.abspath(__file__))
14 | _build_dir = os.path.join(_root_dir, 'frontend/build')
15 |
16 | _awesome_table_ = components.declare_component(
17 | "awesome_table",
18 | path=_build_dir
19 | )
20 | else:
21 | _awesome_table_ = components.declare_component(
22 | "awesome_table",
23 | url='http://localhost:3001'
24 | )
25 |
26 | def __init__(self, data: pd.DataFrame, columns: List =[], show_order = False, show_search= False, show_search_order_in_sidebar = False, key = 'awesome_table'):
27 | """AwesomeTable is a component for Streamlit to build a table based in bootstrap with search and order funcionality.
28 | Can build this table based in a pandas dataframe. The order and search components would be displayed on the sidebar or above the table.
29 |
30 | Args:
31 | data (pd.Dataframe): Dataframe to build the table. If you've a JSON data, you can use the `pd.json_normalize(json)` method.
32 | columns (List, optional): Columns that will be displayed in table. You can pass parameters to personalize each. Defaults to [].
33 | show_order (bool, optional): Show order components. Defaults to False.
34 | show_search (bool, optional): Show search components. Defaults to False.
35 | show_search_order_in_sidebar (bool, optional): [description]. Defaults to False.
36 | key (str, optional): Key for identification table. Defaults to 'awesome_table'.
37 | """
38 | self.data = self.set_data(data, columns)
39 | self.columns = self.set_columns(columns)
40 | self.key = key
41 | self.show_order = show_order
42 | self.show_search = show_search
43 | self.show_search_order_in_sidebar = show_search_order_in_sidebar
44 |
45 | self.build_table_content()
46 |
47 | self.build_order_component()
48 | AwesomeTable._awesome_table_(data=self.table_content, columns=[column.to_json() for column in self.columns], key=self.key)
49 |
50 | def set_data(self, data, columns) -> pd.DataFrame:
51 | """Set dataframe based in columns passed by parameter.
52 |
53 | Args:
54 | data (pd.DataFrame): Dataframe pandas.
55 | columns (List[Column]): List of the columns.
56 |
57 | Returns:
58 | pd.Dataframe: Pandas Dataframe based in columns passed by parameter.
59 | """
60 | if columns is not None and len(columns) > 0:
61 | if type(columns[0]) is str:
62 | data = data[[column for column in columns]]
63 | else:
64 | data = data[[column.name for column in columns]]
65 | for col in [column.name for column in columns if column.dtype == ColumnDType.DATETIME]:
66 | data[col] = pd.to_datetime(data[col])
67 | return data
68 |
69 | def set_columns(self, columns):
70 | """Set columns based in parameters passed by parameter.
71 |
72 | Args:
73 | columns (_type_): _description_
74 |
75 | Returns:
76 | _type_: _description_
77 | """
78 | if columns is None or len(columns) == 0:
79 | self.columns = None
80 | return self.get_columns()
81 | if columns is not None and len(columns) > 0 and type(columns[0]) is str:
82 | return [Column(column) for column in columns]
83 | return columns
84 |
85 | def get_columns(self):
86 | """If columns not passed by parameter, return all columns based in pandas Dataframe columns.
87 |
88 | Returns:
89 | List[Column]: List of columns.
90 | """
91 | if self.columns is None or len(self.columns) == 0:
92 | self.columns = list()
93 | for col in self.data.columns:
94 | self.columns.append(Column(col, dtype=ColumnDType.STRING))
95 | return self.columns
96 |
97 | def get_column_label_by_name(self, name):
98 | """Return the label of the column based in the name passed by parameter.
99 |
100 | Args:
101 | name (str): Name of the column.
102 |
103 | Returns:
104 | str: Return label if exists, else return name.
105 | """
106 | for column in self.get_columns():
107 | if column.name == name:
108 | return column.get_label()
109 | return None
110 |
111 | def get_column_name(self):
112 | """Return all columns names.
113 |
114 | Returns:
115 | List[str]: Columns name
116 | """
117 | return [column.name for column in self.get_columns()]
118 |
119 |
120 | def build_table_content(self):
121 | """Create json to populate table from pandas Dataframe.
122 | """
123 | data = self.data.copy()
124 | for col in [column for column in self.columns if column.dtype == ColumnDType.DATETIME]:
125 | data[col.name] = pd.to_datetime(data[col.name]).dt.strftime(col.dateformat)
126 | self.table_content = data.to_json(path_or_buf=None, index=False, orient='table')
127 |
128 | def order_table(self):
129 | """Order pandas dataframe based in parameters filled in frontend.
130 | """
131 | if self.show_search_order_in_sidebar:
132 | order_column = st.session_state.get('sb_order_column', None)
133 | order_ascending = st.session_state.get('sb_order_ascending', True)
134 | else:
135 | order_column = st.session_state.get('order_column', None)
136 | order_ascending = st.session_state.get('order_ascending', True)
137 |
138 | if order_ascending == 'Ascending':
139 | order_ascending = True
140 | elif order_ascending == 'Descending':
141 | order_ascending = False
142 |
143 | if order_column:
144 | self.data.sort_values(by=[order_column], ascending=order_ascending, inplace=True)
145 | self.build_table_content()
146 |
147 | def search_table(self):
148 | """Search pandas dataframe based in parameters filled in frontend.
149 | """
150 | if self.show_search_order_in_sidebar:
151 | search_text = st.session_state.get('sb_search_text', None)
152 | search_by = st.session_state.get('sb_search_by', None)
153 | else:
154 | search_text = st.session_state.get('search_text', None)
155 | search_by = st.session_state.get('search_by', None)
156 |
157 | if search_text is not None and search_by is not None:
158 | query = search_by+f'.astype("str").str.lower().str.contains("{str(search_text).lower()}")'
159 | self.data.query(query, engine='python', inplace=True)
160 | self.build_table_content()
161 |
162 | def build_order_component(self):
163 | """Build order and search components.
164 | """
165 | if self.show_search_order_in_sidebar:
166 | if self.show_order:
167 | st.sidebar.selectbox('Order by', self.data.columns, format_func=self.get_column_label_by_name, on_change=self.order_table(), key='sb_order_column')
168 | st.sidebar.selectbox('Strategy', ['Ascending','Descending'], on_change=self.order_table(), key='sb_order_ascending')
169 | if self.show_search:
170 | st.sidebar.text_input('Search', on_change=self.search_table(), key='sb_search_text')
171 | st.sidebar.selectbox('by', self.data.columns , format_func=self.get_column_label_by_name, on_change=self.search_table(), key='sb_search_by')
172 | else:
173 | col_order, col_strategy, col_search, col_searchby = st.columns([1,1,2,1])
174 | if self.show_order:
175 | col_order.selectbox('Order by', self.data.columns, format_func=self.get_column_label_by_name, on_change=self.order_table(), key='order_column')
176 | col_strategy.selectbox('Strategy', ['Ascending','Descending'], on_change=self.order_table(), key='order_ascending')
177 | if self.show_search:
178 | col_search.text_input('Search', on_change=self.search_table(), key='search_text')
179 | col_searchby.selectbox('by', self.data.columns , format_func=self.get_column_label_by_name, on_change=self.search_table(), key='search_by')
--------------------------------------------------------------------------------
/awesome_table/column/__init__.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | class ColumnDType(Enum):
4 |
5 | STRING = "STRING"
6 | ICONBUTTON = "ICONBUTTON"
7 | DOWNLOAD = "DOWNLOAD"
8 | IMAGE = "IMAGE"
9 | LINK = "LINK"
10 | DATETIME = "DATETIME"
11 |
12 | class Column():
13 |
14 | def __init__(self, name, label = None, switchcase = None, dtype: ColumnDType = ColumnDType.STRING, icon = None, dateformat = '%Y-%m-%d'):
15 | self.name = name
16 | self.label = label
17 | self.switchcase = switchcase
18 | self.icon = icon
19 | self.dtype = dtype
20 | self.dateformat = dateformat
21 |
22 | def get_label(self):
23 | if self.label is None or self.label == '':
24 | return self.name
25 | else:
26 | return self.label
27 |
28 | def to_json(self):
29 | return {
30 | 'name': self.name,
31 | 'label': self.get_label(),
32 | 'switchcase': self.switchcase,
33 | 'dtype': self.dtype.value,
34 | 'icon' : self.icon,
35 | 'dateformat' : self.dateformat
36 | }
--------------------------------------------------------------------------------
/awesome_table/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "trailingComma": "es5"
5 | }
6 |
--------------------------------------------------------------------------------
/awesome_table/frontend/build/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.js": "./static/js/main.92daa278.chunk.js",
4 | "main.js.map": "./static/js/main.92daa278.chunk.js.map",
5 | "runtime-main.js": "./static/js/runtime-main.11ec9aca.js",
6 | "runtime-main.js.map": "./static/js/runtime-main.11ec9aca.js.map",
7 | "static/css/2.2515e1bc.chunk.css": "./static/css/2.2515e1bc.chunk.css",
8 | "static/js/2.0faa2557.chunk.js": "./static/js/2.0faa2557.chunk.js",
9 | "static/js/2.0faa2557.chunk.js.map": "./static/js/2.0faa2557.chunk.js.map",
10 | "index.html": "./index.html",
11 | "precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js": "./precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js",
12 | "service-worker.js": "./service-worker.js",
13 | "static/css/2.2515e1bc.chunk.css.map": "./static/css/2.2515e1bc.chunk.css.map",
14 | "static/js/2.0faa2557.chunk.js.LICENSE.txt": "./static/js/2.0faa2557.chunk.js.LICENSE.txt",
15 | "static/media/all.css": "./static/media/fa-v4compatibility.9d6f359a.ttf"
16 | },
17 | "entrypoints": [
18 | "static/js/runtime-main.11ec9aca.js",
19 | "static/css/2.2515e1bc.chunk.css",
20 | "static/js/2.0faa2557.chunk.js",
21 | "static/js/main.92daa278.chunk.js"
22 | ]
23 | }
--------------------------------------------------------------------------------
/awesome_table/frontend/build/index.html:
--------------------------------------------------------------------------------
1 |
Streamlit Component
--------------------------------------------------------------------------------
/awesome_table/frontend/build/precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "5c05bf2368f42586d2a8aecd79414e14",
4 | "url": "./index.html"
5 | },
6 | {
7 | "revision": "e4a7f3239a24044ebd4f",
8 | "url": "./static/css/2.2515e1bc.chunk.css"
9 | },
10 | {
11 | "revision": "e4a7f3239a24044ebd4f",
12 | "url": "./static/js/2.0faa2557.chunk.js"
13 | },
14 | {
15 | "revision": "3fc7fb5bfeeec1534560a2c962e360a7",
16 | "url": "./static/js/2.0faa2557.chunk.js.LICENSE.txt"
17 | },
18 | {
19 | "revision": "0797e5b6b0dc59deef75",
20 | "url": "./static/js/main.92daa278.chunk.js"
21 | },
22 | {
23 | "revision": "7c26bca7e16783d14d15",
24 | "url": "./static/js/runtime-main.11ec9aca.js"
25 | },
26 | {
27 | "revision": "ee91e640b5449fb98d9320c877a9866e",
28 | "url": "./static/media/fa-brands-400.ee91e640.woff2"
29 | },
30 | {
31 | "revision": "f34b6a2a94e1a01e4c21fa84dcbf6667",
32 | "url": "./static/media/fa-brands-400.f34b6a2a.ttf"
33 | },
34 | {
35 | "revision": "65e80529f5cfcf16a4b1161601a1616c",
36 | "url": "./static/media/fa-regular-400.65e80529.ttf"
37 | },
38 | {
39 | "revision": "82bafee9dcc7b6fb7bca7ed323f9b7ae",
40 | "url": "./static/media/fa-regular-400.82bafee9.woff2"
41 | },
42 | {
43 | "revision": "52afeb7a328694838c6b073ad7af24d8",
44 | "url": "./static/media/fa-solid-900.52afeb7a.ttf"
45 | },
46 | {
47 | "revision": "57b380d27f14f16e737bcca7e849cf79",
48 | "url": "./static/media/fa-solid-900.57b380d2.woff2"
49 | },
50 | {
51 | "revision": "43044320c62b2b1397b8a0d535dea6a7",
52 | "url": "./static/media/fa-v4compatibility.43044320.woff2"
53 | },
54 | {
55 | "revision": "9d6f359a328ee3df73709c1d8f05b0d4",
56 | "url": "./static/media/fa-v4compatibility.9d6f359a.ttf"
57 | }
58 | ]);
--------------------------------------------------------------------------------
/awesome_table/frontend/build/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "./precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js"
18 | );
19 |
20 | self.addEventListener('message', (event) => {
21 | if (event.data && event.data.type === 'SKIP_WAITING') {
22 | self.skipWaiting();
23 | }
24 | });
25 |
26 | workbox.core.clientsClaim();
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
37 |
38 | blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
39 | });
40 |
--------------------------------------------------------------------------------
/awesome_table/frontend/build/static/js/2.0faa2557.chunk.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /**
8 | * @license
9 | * Copyright 2018-2021 Streamlit Inc.
10 | *
11 | * Licensed under the Apache License, Version 2.0 (the "License");
12 | * you may not use this file except in compliance with the License.
13 | * You may obtain a copy of the License at
14 | *
15 | * http://www.apache.org/licenses/LICENSE-2.0
16 | *
17 | * Unless required by applicable law or agreed to in writing, software
18 | * distributed under the License is distributed on an "AS IS" BASIS,
19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | * See the License for the specific language governing permissions and
21 | * limitations under the License.
22 | */
23 |
24 | /** @license React v0.19.1
25 | * scheduler.production.min.js
26 | *
27 | * Copyright (c) Facebook, Inc. and its affiliates.
28 | *
29 | * This source code is licensed under the MIT license found in the
30 | * LICENSE file in the root directory of this source tree.
31 | */
32 |
33 | /** @license React v16.13.1
34 | * react-is.production.min.js
35 | *
36 | * Copyright (c) Facebook, Inc. and its affiliates.
37 | *
38 | * This source code is licensed under the MIT license found in the
39 | * LICENSE file in the root directory of this source tree.
40 | */
41 |
42 | /** @license React v16.14.0
43 | * react-dom.production.min.js
44 | *
45 | * Copyright (c) Facebook, Inc. and its affiliates.
46 | *
47 | * This source code is licensed under the MIT license found in the
48 | * LICENSE file in the root directory of this source tree.
49 | */
50 |
51 | /** @license React v16.14.0
52 | * react.production.min.js
53 | *
54 | * Copyright (c) Facebook, Inc. and its affiliates.
55 | *
56 | * This source code is licensed under the MIT license found in the
57 | * LICENSE file in the root directory of this source tree.
58 | */
59 |
--------------------------------------------------------------------------------
/awesome_table/frontend/build/static/js/main.92daa278.chunk.js:
--------------------------------------------------------------------------------
1 | (this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[]).push([[0],{15:function(e,t,a){e.exports=a(25)},25:function(e,t,a){"use strict";a.r(t);var n=a(6),r=a.n(n),l=a(13),c=a.n(l),o=a(0),s=a(1),m=a(7),i=a(2),u=a(3),d=a(11),p=function(e){Object(i.a)(a,e);var t=Object(u.a)(a);function a(){var e;Object(s.a)(this,a);for(var n=arguments.length,l=new Array(n),c=0;c {\r\n public state = { numClicks: 0, isFocused: false }\r\n\r\n public render = (): ReactNode => {\r\n // Arguments that are passed to the plugin in Python are accessible\r\n // via `this.props.args`. Here, we access the \"name\" arg.\r\n const data = JSON.parse(this.props.args[\"data\"])\r\n const columns = this.props.args[\"columns\"]\r\n console.log(columns)\r\n\r\n // Streamlit sends us a theme object via props that we can use to ensure\r\n // that our component has visuals that match the active theme in a\r\n // streamlit app.\r\n const { theme } = this.props\r\n const style: React.CSSProperties = {}\r\n\r\n // Maintain compatibility with older versions of Streamlit that don't send\r\n // a theme object.\r\n if (theme) {\r\n // Use the theme object to style our button border. Alternatively, the\r\n // theme style is defined in CSS vars.\r\n const borderStyling = `1px solid ${\r\n this.state.isFocused ? theme.primaryColor : \"gray\"\r\n }`\r\n style.border = borderStyling\r\n style.outline = borderStyling\r\n }\r\n\r\n // Show a button and some text.\r\n // When the button is clicked, we'll increment our \"numClicks\" state\r\n // variable, and send its new value back to Streamlit, where it'll\r\n // be available to the Python program.\r\n return (\r\n \r\n
\r\n \r\n {\r\n columns.map((column: any) => {\r\n return(\r\n {column.label} | \r\n )\r\n })}\r\n \r\n \r\n {data.data.map((data: any) => {\r\n const ICONBUTTON = {\r\n \"display\": \"inline-block\",\r\n \"font-size\": \"1.25rem\",\r\n \"color\": \"#212529\",\r\n \"cursor\": \"pointer\",\r\n \"user-select\": \"none\",\r\n \"padding\": \"0.5rem 1rem\"\r\n }\r\n const IMAGE = {\r\n \"height\": 50,\r\n }\r\n return (\r\n {\r\n columns.map((column: any) => {\r\n if(column.dtype === \"STRING\" || column.dtype === \"DATETIME\") {\r\n return (\r\n {data[column.name]} | \r\n )\r\n }\r\n if(column.dtype === \"ICONBUTTON\") {\r\n return (\r\n \r\n \r\n | \r\n )\r\n }\r\n if(column.dtype === \"DOWNLOAD\") {\r\n return (\r\n | \r\n )\r\n }\r\n if(column.dtype === \"LINK\") {\r\n return (\r\n | \r\n )\r\n }\r\n if(column.dtype === \"IMAGE\") {\r\n return(\r\n  | \r\n )\r\n }\r\n return ( | )\r\n }, this)}\r\n
\r\n );\r\n })}\r\n \r\n
\r\n
\r\n )\r\n }\r\n\r\n /** Click handler for our \"Click Me!\" button. */\r\n private onClicked = (): void => {\r\n // Increment state.numClicks, and pass the new value back to\r\n // Streamlit via `Streamlit.setComponentValue`.\r\n this.setState(\r\n prevState => ({ numClicks: prevState.numClicks + 1 }),\r\n () => Streamlit.setComponentValue(this.state.numClicks)\r\n )\r\n }\r\n\r\n /** Focus handler for our \"Click Me!\" button. */\r\n private _onFocus = (): void => {\r\n this.setState({ isFocused: true })\r\n }\r\n\r\n /** Blur handler for our \"Click Me!\" button. */\r\n private _onBlur = (): void => {\r\n this.setState({ isFocused: false })\r\n }\r\n}\r\n\r\n// \"withStreamlitConnection\" is a wrapper function. It bootstraps the\r\n// connection between your component and the Streamlit app, and handles\r\n// passing arguments from Python -> Component.\r\n//\r\n// You don't need to edit withStreamlitConnection (but you're welcome to!).\r\nexport default withStreamlitConnection(AwesomeTable)\r\n","import React from \"react\"\r\nimport ReactDOM from \"react-dom\"\r\nimport AwesomeTable from \"./AwesomeTable\"\r\nimport 'bootstrap/dist/css/bootstrap.min.css'\r\nimport '@fortawesome/fontawesome-free/css/all.css'\r\n\r\nReactDOM.render(\r\n \r\n \r\n ,\r\n document.getElementById(\"root\")\r\n)\r\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/awesome_table/frontend/build/static/js/runtime-main.11ec9aca.js:
--------------------------------------------------------------------------------
1 | !function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "homepage": "."
43 | }
44 |
--------------------------------------------------------------------------------
/awesome_table/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Streamlit Component
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/awesome_table/frontend/src/AwesomeTable.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Streamlit,
3 | StreamlitComponentBase,
4 | withStreamlitConnection,
5 | } from "streamlit-component-lib"
6 | import React, { ReactNode } from "react"
7 |
8 | interface State {
9 | numClicks: number
10 | isFocused: boolean
11 | }
12 |
13 | /**
14 | * This is a React-based component template. The `render()` function is called
15 | * automatically when your component should be re-rendered.
16 | */
17 | class AwesomeTable extends StreamlitComponentBase {
18 | public state = { numClicks: 0, isFocused: false }
19 |
20 | public render = (): ReactNode => {
21 | // Arguments that are passed to the plugin in Python are accessible
22 | // via `this.props.args`. Here, we access the "name" arg.
23 | const data = JSON.parse(this.props.args["data"])
24 | const columns = this.props.args["columns"]
25 | console.log(columns)
26 |
27 | // Streamlit sends us a theme object via props that we can use to ensure
28 | // that our component has visuals that match the active theme in a
29 | // streamlit app.
30 | const { theme } = this.props
31 | const style: React.CSSProperties = {}
32 |
33 | // Maintain compatibility with older versions of Streamlit that don't send
34 | // a theme object.
35 | if (theme) {
36 | // Use the theme object to style our button border. Alternatively, the
37 | // theme style is defined in CSS vars.
38 | const borderStyling = `1px solid ${
39 | this.state.isFocused ? theme.primaryColor : "gray"
40 | }`
41 | style.border = borderStyling
42 | style.outline = borderStyling
43 | }
44 |
45 | // Show a button and some text.
46 | // When the button is clicked, we'll increment our "numClicks" state
47 | // variable, and send its new value back to Streamlit, where it'll
48 | // be available to the Python program.
49 | return (
50 |
51 |
52 |
53 | {
54 | columns.map((column: any) => {
55 | return(
56 | {column.label} |
57 | )
58 | })}
59 |
60 |
61 | {data.data.map((data: any) => {
62 | const ICONBUTTON = {
63 | "display": "inline-block",
64 | "font-size": "1.25rem",
65 | "color": "#212529",
66 | "cursor": "pointer",
67 | "user-select": "none",
68 | "padding": "0.5rem 1rem"
69 | }
70 | const IMAGE = {
71 | "height": 50,
72 | }
73 | return (
74 | {
75 | columns.map((column: any) => {
76 | if(column.dtype === "STRING" || column.dtype === "DATETIME") {
77 | return (
78 | {data[column.name]} |
79 | )
80 | }
81 | if(column.dtype === "ICONBUTTON") {
82 | return (
83 |
84 |
85 | |
86 | )
87 | }
88 | if(column.dtype === "DOWNLOAD") {
89 | return (
90 | |
91 | )
92 | }
93 | if(column.dtype === "LINK") {
94 | return (
95 | |
96 | )
97 | }
98 | if(column.dtype === "IMAGE") {
99 | return(
100 |  |
101 | )
102 | }
103 | return ( | )
104 | }, this)}
105 |
106 | );
107 | })}
108 |
109 |
110 |
111 | )
112 | }
113 |
114 | /** Click handler for our "Click Me!" button. */
115 | private onClicked = (): void => {
116 | // Increment state.numClicks, and pass the new value back to
117 | // Streamlit via `Streamlit.setComponentValue`.
118 | this.setState(
119 | prevState => ({ numClicks: prevState.numClicks + 1 }),
120 | () => Streamlit.setComponentValue(this.state.numClicks)
121 | )
122 | }
123 |
124 | /** Focus handler for our "Click Me!" button. */
125 | private _onFocus = (): void => {
126 | this.setState({ isFocused: true })
127 | }
128 |
129 | /** Blur handler for our "Click Me!" button. */
130 | private _onBlur = (): void => {
131 | this.setState({ isFocused: false })
132 | }
133 | }
134 |
135 | // "withStreamlitConnection" is a wrapper function. It bootstraps the
136 | // connection between your component and the Streamlit app, and handles
137 | // passing arguments from Python -> Component.
138 | //
139 | // You don't need to edit withStreamlitConnection (but you're welcome to!).
140 | export default withStreamlitConnection(AwesomeTable)
141 |
--------------------------------------------------------------------------------
/awesome_table/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import ReactDOM from "react-dom"
3 | import AwesomeTable from "./AwesomeTable"
4 | import 'bootstrap/dist/css/bootstrap.min.css'
5 | import '@fortawesome/fontawesome-free/css/all.css'
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById("root")
12 | )
13 |
--------------------------------------------------------------------------------
/awesome_table/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/awesome_table/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react"
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------
/build/lib/awesome_table/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | from pandas.io.json import json_normalize
4 | import streamlit as st
5 | from typing import List
6 | import streamlit.components.v1 as components
7 | from awesome_table.column import (ColumnDType, Column)
8 |
9 | _RELEASE = True
10 |
11 | class AwesomeTable():
12 | """AwesomeTable is a component for Streamlit to build a table based in bootstrap with search and order funcionality."""
13 | if _RELEASE:
14 | _root_dir = os.path.dirname(os.path.abspath(__file__))
15 | _build_dir = os.path.join(_root_dir, 'frontend/build')
16 |
17 | _awesome_table_ = components.declare_component(
18 | "awesome_table",
19 | path=_build_dir
20 | )
21 | else:
22 | _awesome_table_ = components.declare_component(
23 | "awesome_table",
24 | url='http://localhost:3001'
25 | )
26 |
27 | def __init__(self, data: pd.DataFrame, columns: List =[], show_order = False, show_search= False, show_search_order_in_sidebar = False, key = 'awesome_table'):
28 | """AwesomeTable is a component for Streamlit to build a table based in bootstrap with search and order funcionality.
29 | Can build this table based in a pandas dataframe. The order and search components would be displayed on the sidebar or above the table.
30 |
31 | Args:
32 | data (pd.Dataframe): Dataframe to build the table. If you've a JSON data, you can use the `pd.json_normalize(json)` method.
33 | columns (List, optional): Columns that will be displayed in table. You can pass parameters to personalize each. Defaults to [].
34 | show_order (bool, optional): Show order components. Defaults to False.
35 | show_search (bool, optional): Show search components. Defaults to False.
36 | show_search_order_in_sidebar (bool, optional): [description]. Defaults to False.
37 | key (str, optional): Key for identification table. Defaults to 'awesome_table'.
38 | """
39 | self.data = self.set_data(data, columns)
40 | self.columns = self.set_columns(columns)
41 | self.key = key
42 | self.show_order = show_order
43 | self.show_search = show_search
44 | self.show_search_order_in_sidebar = show_search_order_in_sidebar
45 |
46 | self.build_table_content()
47 |
48 | self.build_order_component()
49 | AwesomeTable._awesome_table_(data=self.table_content, columns=[column.to_json() for column in self.columns], key=self.key)
50 |
51 | def set_data(self, data, columns) -> pd.DataFrame:
52 | """Set dataframe based in columns passed by parameter.
53 |
54 | Args:
55 | data (pd.DataFrame): Dataframe pandas.
56 | columns (List[Column]): List of the columns.
57 |
58 | Returns:
59 | pd.Dataframe: Pandas Dataframe based in columns passed by parameter.
60 | """
61 | if columns is not None and len(columns) > 0:
62 | if type(columns[0]) is str:
63 | data = data[[column for column in columns]]
64 | else:
65 | data = data[[column.name for column in columns]]
66 | for col in [column.name for column in columns if column.dtype == ColumnDType.DATETIME]:
67 | data[col] = pd.to_datetime(data[col])
68 | return data
69 |
70 | def set_columns(self, columns):
71 | """Set columns based in parameters passed by parameter.
72 |
73 | Args:
74 | columns (_type_): _description_
75 |
76 | Returns:
77 | _type_: _description_
78 | """
79 | if columns is None or len(columns) == 0:
80 | self.columns = None
81 | return self.get_columns()
82 | if columns is not None and len(columns) > 0 and type(columns[0]) is str:
83 | return [Column(column) for column in columns]
84 | return columns
85 |
86 | def get_columns(self):
87 | """If columns not passed by parameter, return all columns based in pandas Dataframe columns.
88 |
89 | Returns:
90 | List[Column]: List of columns.
91 | """
92 | if self.columns is None or len(self.columns) == 0:
93 | self.columns = list()
94 | for col in self.data.columns:
95 | self.columns.append(Column(col, dtype=ColumnDType.STRING))
96 | return self.columns
97 |
98 | def get_column_label_by_name(self, name):
99 | """Return the label of the column based in the name passed by parameter.
100 |
101 | Args:
102 | name (str): Name of the column.
103 |
104 | Returns:
105 | str: Return label if exists, else return name.
106 | """
107 | for column in self.get_columns():
108 | if column.name == name:
109 | return column.get_label()
110 | return None
111 |
112 | def get_column_name(self):
113 | """Return all columns names.
114 |
115 | Returns:
116 | List[str]: Columns name
117 | """
118 | return [column.name for column in self.get_columns()]
119 |
120 |
121 | def build_table_content(self):
122 | """Create json to populate table from pandas Dataframe.
123 | """
124 | data = self.data.copy()
125 | for col in [column for column in self.columns if column.dtype == ColumnDType.DATETIME]:
126 | data[col.name] = pd.to_datetime(data[col.name]).dt.strftime(col.dateformat)
127 | self.table_content = data.to_json(path_or_buf=None, index=False, orient='table')
128 |
129 | def order_table(self):
130 | """Order pandas dataframe based in parameters filled in frontend.
131 | """
132 | if self.show_search_order_in_sidebar:
133 | order_column = st.session_state.get('sb_order_column', None)
134 | order_ascending = st.session_state.get('sb_order_ascending', True)
135 | else:
136 | order_column = st.session_state.get('order_column', None)
137 | order_ascending = st.session_state.get('order_ascending', True)
138 |
139 | if order_ascending == 'Ascending':
140 | order_ascending = True
141 | elif order_ascending == 'Descending':
142 | order_ascending = False
143 |
144 | if order_column:
145 | self.data.sort_values(by=[order_column], ascending=order_ascending, inplace=True)
146 | self.build_table_content()
147 |
148 | def search_table(self):
149 | """Search pandas dataframe based in parameters filled in frontend.
150 | """
151 | if self.show_search_order_in_sidebar:
152 | search_text = st.session_state.get('sb_search_text', None)
153 | search_by = st.session_state.get('sb_search_by', None)
154 | else:
155 | search_text = st.session_state.get('search_text', None)
156 | search_by = st.session_state.get('search_by', None)
157 |
158 | if search_text is not None and search_by is not None:
159 | query = search_by+f'.astype("str").str.lower().str.contains("{str(search_text).lower()}")'
160 | self.data.query(query, engine='python', inplace=True)
161 | self.build_table_content()
162 |
163 | def build_order_component(self):
164 | """Build order and search components.
165 | """
166 | if self.show_search_order_in_sidebar:
167 | if self.show_order:
168 | st.sidebar.selectbox('Order by', self.data.columns, format_func=self.get_column_label_by_name, on_change=self.order_table(), key='sb_order_column')
169 | st.sidebar.selectbox('Strategy', ['Ascending','Descending'], on_change=self.order_table(), key='sb_order_ascending')
170 | if self.show_search:
171 | st.sidebar.text_input('Search', on_change=self.search_table(), key='sb_search_text')
172 | st.sidebar.selectbox('by', self.data.columns , format_func=self.get_column_label_by_name, on_change=self.search_table(), key='sb_search_by')
173 | else:
174 | col_order, col_strategy, col_search, col_searchby = st.columns([1,1,2,1])
175 | if self.show_order:
176 | col_order.selectbox('Order by', self.data.columns, format_func=self.get_column_label_by_name, on_change=self.order_table(), key='order_column')
177 | col_strategy.selectbox('Strategy', ['Ascending','Descending'], on_change=self.order_table(), key='order_ascending')
178 | if self.show_search:
179 | col_search.text_input('Search', on_change=self.search_table(), key='search_text')
180 | col_searchby.selectbox('by', self.data.columns , format_func=self.get_column_label_by_name, on_change=self.search_table(), key='search_by')
--------------------------------------------------------------------------------
/build/lib/awesome_table/column/__init__.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 | class ColumnDType(Enum):
4 |
5 | STRING = "STRING"
6 | ICONBUTTON = "ICONBUTTON"
7 | DOWNLOAD = "DOWNLOAD"
8 | IMAGE = "IMAGE"
9 | LINK = "LINK"
10 | DATETIME = "DATETIME"
11 |
12 | class Column():
13 |
14 | def __init__(self, name, label = None, switchcase = None, dtype: ColumnDType = ColumnDType.STRING, icon = None, dateformat = '%Y-%m-%d'):
15 | self.name = name
16 | self.label = label
17 | self.switchcase = switchcase
18 | self.icon = icon
19 | self.dtype = dtype
20 | self.dateformat = dateformat
21 |
22 | def get_label(self):
23 | if self.label is None or self.label == '':
24 | return self.name
25 | else:
26 | return self.label
27 |
28 | def to_json(self):
29 | return {
30 | 'name': self.name,
31 | 'label': self.get_label(),
32 | 'switchcase': self.switchcase,
33 | 'dtype': self.dtype.value,
34 | 'icon' : self.icon,
35 | 'dateformat' : self.dateformat
36 | }
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | "main.js": "./static/js/main.92daa278.chunk.js",
4 | "main.js.map": "./static/js/main.92daa278.chunk.js.map",
5 | "runtime-main.js": "./static/js/runtime-main.11ec9aca.js",
6 | "runtime-main.js.map": "./static/js/runtime-main.11ec9aca.js.map",
7 | "static/css/2.2515e1bc.chunk.css": "./static/css/2.2515e1bc.chunk.css",
8 | "static/js/2.0faa2557.chunk.js": "./static/js/2.0faa2557.chunk.js",
9 | "static/js/2.0faa2557.chunk.js.map": "./static/js/2.0faa2557.chunk.js.map",
10 | "index.html": "./index.html",
11 | "precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js": "./precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js",
12 | "service-worker.js": "./service-worker.js",
13 | "static/css/2.2515e1bc.chunk.css.map": "./static/css/2.2515e1bc.chunk.css.map",
14 | "static/js/2.0faa2557.chunk.js.LICENSE.txt": "./static/js/2.0faa2557.chunk.js.LICENSE.txt",
15 | "static/media/all.css": "./static/media/fa-v4compatibility.9d6f359a.ttf"
16 | },
17 | "entrypoints": [
18 | "static/js/runtime-main.11ec9aca.js",
19 | "static/css/2.2515e1bc.chunk.css",
20 | "static/js/2.0faa2557.chunk.js",
21 | "static/js/main.92daa278.chunk.js"
22 | ]
23 | }
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/index.html:
--------------------------------------------------------------------------------
1 | Streamlit Component
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = (self.__precacheManifest || []).concat([
2 | {
3 | "revision": "5c05bf2368f42586d2a8aecd79414e14",
4 | "url": "./index.html"
5 | },
6 | {
7 | "revision": "e4a7f3239a24044ebd4f",
8 | "url": "./static/css/2.2515e1bc.chunk.css"
9 | },
10 | {
11 | "revision": "e4a7f3239a24044ebd4f",
12 | "url": "./static/js/2.0faa2557.chunk.js"
13 | },
14 | {
15 | "revision": "3fc7fb5bfeeec1534560a2c962e360a7",
16 | "url": "./static/js/2.0faa2557.chunk.js.LICENSE.txt"
17 | },
18 | {
19 | "revision": "0797e5b6b0dc59deef75",
20 | "url": "./static/js/main.92daa278.chunk.js"
21 | },
22 | {
23 | "revision": "7c26bca7e16783d14d15",
24 | "url": "./static/js/runtime-main.11ec9aca.js"
25 | },
26 | {
27 | "revision": "ee91e640b5449fb98d9320c877a9866e",
28 | "url": "./static/media/fa-brands-400.ee91e640.woff2"
29 | },
30 | {
31 | "revision": "f34b6a2a94e1a01e4c21fa84dcbf6667",
32 | "url": "./static/media/fa-brands-400.f34b6a2a.ttf"
33 | },
34 | {
35 | "revision": "65e80529f5cfcf16a4b1161601a1616c",
36 | "url": "./static/media/fa-regular-400.65e80529.ttf"
37 | },
38 | {
39 | "revision": "82bafee9dcc7b6fb7bca7ed323f9b7ae",
40 | "url": "./static/media/fa-regular-400.82bafee9.woff2"
41 | },
42 | {
43 | "revision": "52afeb7a328694838c6b073ad7af24d8",
44 | "url": "./static/media/fa-solid-900.52afeb7a.ttf"
45 | },
46 | {
47 | "revision": "57b380d27f14f16e737bcca7e849cf79",
48 | "url": "./static/media/fa-solid-900.57b380d2.woff2"
49 | },
50 | {
51 | "revision": "43044320c62b2b1397b8a0d535dea6a7",
52 | "url": "./static/media/fa-v4compatibility.43044320.woff2"
53 | },
54 | {
55 | "revision": "9d6f359a328ee3df73709c1d8f05b0d4",
56 | "url": "./static/media/fa-v4compatibility.9d6f359a.ttf"
57 | }
58 | ]);
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "./precache-manifest.b80423b41e280a5fb5cd92c8b1f3ca9e.js"
18 | );
19 |
20 | self.addEventListener('message', (event) => {
21 | if (event.data && event.data.type === 'SKIP_WAITING') {
22 | self.skipWaiting();
23 | }
24 | });
25 |
26 | workbox.core.clientsClaim();
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
37 |
38 | blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
39 | });
40 |
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/static/js/2.0faa2557.chunk.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /**
8 | * @license
9 | * Copyright 2018-2021 Streamlit Inc.
10 | *
11 | * Licensed under the Apache License, Version 2.0 (the "License");
12 | * you may not use this file except in compliance with the License.
13 | * You may obtain a copy of the License at
14 | *
15 | * http://www.apache.org/licenses/LICENSE-2.0
16 | *
17 | * Unless required by applicable law or agreed to in writing, software
18 | * distributed under the License is distributed on an "AS IS" BASIS,
19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | * See the License for the specific language governing permissions and
21 | * limitations under the License.
22 | */
23 |
24 | /** @license React v0.19.1
25 | * scheduler.production.min.js
26 | *
27 | * Copyright (c) Facebook, Inc. and its affiliates.
28 | *
29 | * This source code is licensed under the MIT license found in the
30 | * LICENSE file in the root directory of this source tree.
31 | */
32 |
33 | /** @license React v16.13.1
34 | * react-is.production.min.js
35 | *
36 | * Copyright (c) Facebook, Inc. and its affiliates.
37 | *
38 | * This source code is licensed under the MIT license found in the
39 | * LICENSE file in the root directory of this source tree.
40 | */
41 |
42 | /** @license React v16.14.0
43 | * react-dom.production.min.js
44 | *
45 | * Copyright (c) Facebook, Inc. and its affiliates.
46 | *
47 | * This source code is licensed under the MIT license found in the
48 | * LICENSE file in the root directory of this source tree.
49 | */
50 |
51 | /** @license React v16.14.0
52 | * react.production.min.js
53 | *
54 | * Copyright (c) Facebook, Inc. and its affiliates.
55 | *
56 | * This source code is licensed under the MIT license found in the
57 | * LICENSE file in the root directory of this source tree.
58 | */
59 |
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/static/js/main.92daa278.chunk.js:
--------------------------------------------------------------------------------
1 | (this.webpackJsonpstreamlit_component_template=this.webpackJsonpstreamlit_component_template||[]).push([[0],{15:function(e,t,a){e.exports=a(25)},25:function(e,t,a){"use strict";a.r(t);var n=a(6),r=a.n(n),l=a(13),c=a.n(l),o=a(0),s=a(1),m=a(7),i=a(2),u=a(3),d=a(11),p=function(e){Object(i.a)(a,e);var t=Object(u.a)(a);function a(){var e;Object(s.a)(this,a);for(var n=arguments.length,l=new Array(n),c=0;c {\r\n public state = { numClicks: 0, isFocused: false }\r\n\r\n public render = (): ReactNode => {\r\n // Arguments that are passed to the plugin in Python are accessible\r\n // via `this.props.args`. Here, we access the \"name\" arg.\r\n const data = JSON.parse(this.props.args[\"data\"])\r\n const columns = this.props.args[\"columns\"]\r\n console.log(columns)\r\n\r\n // Streamlit sends us a theme object via props that we can use to ensure\r\n // that our component has visuals that match the active theme in a\r\n // streamlit app.\r\n const { theme } = this.props\r\n const style: React.CSSProperties = {}\r\n\r\n // Maintain compatibility with older versions of Streamlit that don't send\r\n // a theme object.\r\n if (theme) {\r\n // Use the theme object to style our button border. Alternatively, the\r\n // theme style is defined in CSS vars.\r\n const borderStyling = `1px solid ${\r\n this.state.isFocused ? theme.primaryColor : \"gray\"\r\n }`\r\n style.border = borderStyling\r\n style.outline = borderStyling\r\n }\r\n\r\n // Show a button and some text.\r\n // When the button is clicked, we'll increment our \"numClicks\" state\r\n // variable, and send its new value back to Streamlit, where it'll\r\n // be available to the Python program.\r\n return (\r\n \r\n
\r\n \r\n {\r\n columns.map((column: any) => {\r\n return(\r\n {column.label} | \r\n )\r\n })}\r\n \r\n \r\n {data.data.map((data: any) => {\r\n const ICONBUTTON = {\r\n \"display\": \"inline-block\",\r\n \"font-size\": \"1.25rem\",\r\n \"color\": \"#212529\",\r\n \"cursor\": \"pointer\",\r\n \"user-select\": \"none\",\r\n \"padding\": \"0.5rem 1rem\"\r\n }\r\n const IMAGE = {\r\n \"height\": 50,\r\n }\r\n return (\r\n {\r\n columns.map((column: any) => {\r\n if(column.dtype === \"STRING\" || column.dtype === \"DATETIME\") {\r\n return (\r\n {data[column.name]} | \r\n )\r\n }\r\n if(column.dtype === \"ICONBUTTON\") {\r\n return (\r\n \r\n \r\n | \r\n )\r\n }\r\n if(column.dtype === \"DOWNLOAD\") {\r\n return (\r\n | \r\n )\r\n }\r\n if(column.dtype === \"LINK\") {\r\n return (\r\n | \r\n )\r\n }\r\n if(column.dtype === \"IMAGE\") {\r\n return(\r\n  | \r\n )\r\n }\r\n return ( | )\r\n }, this)}\r\n
\r\n );\r\n })}\r\n \r\n
\r\n
\r\n )\r\n }\r\n\r\n /** Click handler for our \"Click Me!\" button. */\r\n private onClicked = (): void => {\r\n // Increment state.numClicks, and pass the new value back to\r\n // Streamlit via `Streamlit.setComponentValue`.\r\n this.setState(\r\n prevState => ({ numClicks: prevState.numClicks + 1 }),\r\n () => Streamlit.setComponentValue(this.state.numClicks)\r\n )\r\n }\r\n\r\n /** Focus handler for our \"Click Me!\" button. */\r\n private _onFocus = (): void => {\r\n this.setState({ isFocused: true })\r\n }\r\n\r\n /** Blur handler for our \"Click Me!\" button. */\r\n private _onBlur = (): void => {\r\n this.setState({ isFocused: false })\r\n }\r\n}\r\n\r\n// \"withStreamlitConnection\" is a wrapper function. It bootstraps the\r\n// connection between your component and the Streamlit app, and handles\r\n// passing arguments from Python -> Component.\r\n//\r\n// You don't need to edit withStreamlitConnection (but you're welcome to!).\r\nexport default withStreamlitConnection(AwesomeTable)\r\n","import React from \"react\"\r\nimport ReactDOM from \"react-dom\"\r\nimport AwesomeTable from \"./AwesomeTable\"\r\nimport 'bootstrap/dist/css/bootstrap.min.css'\r\nimport '@fortawesome/fontawesome-free/css/all.css'\r\n\r\nReactDOM.render(\r\n \r\n \r\n ,\r\n document.getElementById(\"root\")\r\n)\r\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/build/lib/awesome_table/frontend/build/static/js/runtime-main.11ec9aca.js:
--------------------------------------------------------------------------------
1 | !function(e){function t(t){for(var n,l,a=t[0],p=t[1],i=t[2],c=0,s=[];c= 3.6
17 | setup_requires =
18 | setuptools >= 40.8.0
19 | wheel
20 | install_requires =
21 | streamlit >= 0.63
22 | pandas
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | setuptools.setup(
4 | name="streamlit-awesome-table",
5 | version="0.1.0",
6 | author="Caio Fábio de Araujo",
7 | author_email="caiofaar@gmail.com",
8 | description="Awesome Table is a component to display a table in Streamlit with search and order.",
9 | long_description="Display a table with search and order components that will be display above the table or in sidebar.",
10 | long_description_content_type="text/plain",
11 | url="https://github.com/caiodearaujo/streamlit-awesome-table",
12 | packages=setuptools.find_packages(),
13 | include_package_data=True,
14 | classifiers=[],
15 | python_requires=">=3.6",
16 | install_requires=[
17 | # By definition, a Custom Component depends on Streamlit.
18 | # If your component has other Python dependencies, list
19 | # them here.
20 | "streamlit >= 0.63",
21 | "pandas"
22 | ],
23 | )
24 |
--------------------------------------------------------------------------------