├── .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 | ![Simple Table w/ AwesomeTable](/samples/simple_table/awesome-table-simple.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_columns/awesome-table-with-columns.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_iconbutton/awesome-table-with-iconbutton.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_order/awesome-table-with-order.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_search/awesome-table-with-search.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_sidebar/awesome-table-with-sidebar.png) 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 | ![Simple Table w/ AwesomeTable](/samples/with_image/awesome-table-with-image.png) 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 \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 \r\n )\r\n }\r\n if(column.dtype === \"ICONBUTTON\") {\r\n return (\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
{column.label}
{data[column.name]}\r\n \r\n {column.name}/
\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 | 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 | 79 | ) 80 | } 81 | if(column.dtype === "ICONBUTTON") { 82 | return ( 83 | 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 |
{column.label}
{data[column.name]} 84 | 85 | {column.name}/
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 \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 \r\n )\r\n }\r\n if(column.dtype === \"ICONBUTTON\") {\r\n return (\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
{column.label}
{data[column.name]}\r\n \r\n {column.name}/
\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 | --------------------------------------------------------------------------------