1
0

table_documentation.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. from nicegui import ui
  2. from . import doc
  3. @doc.demo(ui.table)
  4. def main_demo() -> None:
  5. columns = [
  6. {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},
  7. {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},
  8. ]
  9. rows = [
  10. {'name': 'Alice', 'age': 18},
  11. {'name': 'Bob', 'age': 21},
  12. {'name': 'Carol'},
  13. ]
  14. ui.table(columns=columns, rows=rows, row_key='name')
  15. @doc.demo('Table with expandable rows', '''
  16. Scoped slots can be used to insert buttons that toggle the expand state of a table row.
  17. See the [Quasar documentation](https://quasar.dev/vue-components/table#expanding-rows) for more information.
  18. ''')
  19. def table_with_expandable_rows():
  20. columns = [
  21. {'name': 'name', 'label': 'Name', 'field': 'name'},
  22. {'name': 'age', 'label': 'Age', 'field': 'age'},
  23. ]
  24. rows = [
  25. {'name': 'Alice', 'age': 18},
  26. {'name': 'Bob', 'age': 21},
  27. {'name': 'Carol'},
  28. ]
  29. table = ui.table(columns=columns, rows=rows, row_key='name').classes('w-72')
  30. table.add_slot('header', r'''
  31. <q-tr :props="props">
  32. <q-th auto-width />
  33. <q-th v-for="col in props.cols" :key="col.name" :props="props">
  34. {{ col.label }}
  35. </q-th>
  36. </q-tr>
  37. ''')
  38. table.add_slot('body', r'''
  39. <q-tr :props="props">
  40. <q-td auto-width>
  41. <q-btn size="sm" color="accent" round dense
  42. @click="props.expand = !props.expand"
  43. :icon="props.expand ? 'remove' : 'add'" />
  44. </q-td>
  45. <q-td v-for="col in props.cols" :key="col.name" :props="props">
  46. {{ col.value }}
  47. </q-td>
  48. </q-tr>
  49. <q-tr v-show="props.expand" :props="props">
  50. <q-td colspan="100%">
  51. <div class="text-left">This is {{ props.row.name }}.</div>
  52. </q-td>
  53. </q-tr>
  54. ''')
  55. @doc.demo('Show and hide columns', '''
  56. Here is an example of how to show and hide columns in a table.
  57. ''')
  58. def show_and_hide_columns():
  59. from typing import Dict
  60. columns = [
  61. {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},
  62. {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},
  63. ]
  64. rows = [
  65. {'name': 'Alice', 'age': 18},
  66. {'name': 'Bob', 'age': 21},
  67. {'name': 'Carol'},
  68. ]
  69. table = ui.table(columns=columns, rows=rows, row_key='name')
  70. def toggle(column: Dict, visible: bool) -> None:
  71. column['classes'] = '' if visible else 'hidden'
  72. column['headerClasses'] = '' if visible else 'hidden'
  73. table.update()
  74. with ui.button(icon='menu'):
  75. with ui.menu(), ui.column().classes('gap-0 p-2'):
  76. for column in columns:
  77. ui.switch(column['label'], value=True, on_change=lambda e,
  78. column=column: toggle(column, e.value))
  79. @doc.demo('Table with drop down selection', '''
  80. Here is an example of how to use a drop down selection in a table.
  81. After emitting a `rename` event from the scoped slot, the `rename` function updates the table rows.
  82. ''')
  83. def table_with_drop_down_selection():
  84. from nicegui import events
  85. columns = [
  86. {'name': 'name', 'label': 'Name', 'field': 'name'},
  87. {'name': 'age', 'label': 'Age', 'field': 'age'},
  88. ]
  89. rows = [
  90. {'id': 0, 'name': 'Alice', 'age': 18},
  91. {'id': 1, 'name': 'Bob', 'age': 21},
  92. {'id': 2, 'name': 'Carol'},
  93. ]
  94. name_options = ['Alice', 'Bob', 'Carol']
  95. def rename(e: events.GenericEventArguments) -> None:
  96. for row in rows:
  97. if row['id'] == e.args['id']:
  98. row['name'] = e.args['name']
  99. ui.notify(f'Table.rows is now: {table.rows}')
  100. table = ui.table(columns=columns, rows=rows).classes('w-full')
  101. table.add_slot('body-cell-name', r'''
  102. <q-td key="name" :props="props">
  103. <q-select
  104. v-model="props.row.name"
  105. :options="''' + str(name_options) + r'''"
  106. @update:model-value="() => $parent.$emit('rename', props.row)"
  107. />
  108. </q-td>
  109. ''')
  110. table.on('rename', rename)
  111. @doc.demo('Table from Pandas DataFrame', '''
  112. You can create a table from a Pandas DataFrame using the `from_pandas` method.
  113. This method takes a Pandas DataFrame as input and returns a table.
  114. ''')
  115. def table_from_pandas_demo():
  116. import pandas as pd
  117. df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
  118. ui.table.from_pandas(df).classes('max-h-40')
  119. @doc.demo('Adding rows', '''
  120. It's simple to add new rows with the `add_rows(dict)` method.
  121. With the "virtual-scroll" prop set, the table can be programmatically scrolled with the `scrollTo` JavaScript function.
  122. ''')
  123. def adding_rows():
  124. from datetime import datetime
  125. def add():
  126. table.add_rows({'date': datetime.now().strftime('%c')})
  127. table.run_method('scrollTo', len(table.rows)-1)
  128. columns = [{'name': 'date', 'label': 'Date', 'field': 'date'}]
  129. table = ui.table(columns=columns, rows=[]).classes('h-52').props('virtual-scroll')
  130. ui.button('Add row', on_click=add)
  131. @doc.demo('Custom sorting and formatting', '''
  132. You can define dynamic column attributes using a `:` prefix.
  133. This way you can define custom sorting and formatting functions.
  134. The following example allows sorting the `name` column by length.
  135. The `age` column is formatted to show the age in years.
  136. ''')
  137. def custom_formatting():
  138. columns = [
  139. {
  140. 'name': 'name',
  141. 'label': 'Name',
  142. 'field': 'name',
  143. 'sortable': True,
  144. ':sort': '(a, b, rowA, rowB) => b.length - a.length',
  145. },
  146. {
  147. 'name': 'age',
  148. 'label': 'Age',
  149. 'field': 'age',
  150. ':format': 'value => value + " years"',
  151. },
  152. ]
  153. rows = [
  154. {'name': 'Alice', 'age': 18},
  155. {'name': 'Bob', 'age': 21},
  156. {'name': 'Carl', 'age': 42},
  157. ]
  158. ui.table(columns=columns, rows=rows, row_key='name')
  159. @doc.demo('Toggle fullscreen', '''
  160. You can toggle the fullscreen mode of a table using the `toggle_fullscreen()` method.
  161. ''')
  162. def toggle_fullscreen():
  163. table = ui.table(
  164. columns=[{'name': 'name', 'label': 'Name', 'field': 'name'}],
  165. rows=[{'name': 'Alice'}, {'name': 'Bob'}, {'name': 'Carol'}],
  166. ).classes('w-full')
  167. with table.add_slot('top-left'):
  168. def toggle() -> None:
  169. table.toggle_fullscreen()
  170. button.props('icon=fullscreen_exit' if table.is_fullscreen else 'icon=fullscreen')
  171. button = ui.button('Toggle fullscreen', icon='fullscreen', on_click=toggle).props('flat')
  172. @doc.demo('Pagination', '''
  173. You can provide either a single integer or a dictionary to define pagination.
  174. The dictionary can contain the following keys:
  175. - `rowsPerPage`: The number of rows per page.
  176. - `sortBy`: The column name to sort by.
  177. - `descending`: Whether to sort in descending order.
  178. - `page`: The current page (1-based).
  179. ''')
  180. def pagination() -> None:
  181. columns = [
  182. {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},
  183. {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},
  184. ]
  185. rows = [
  186. {'name': 'Elsa', 'age': 18},
  187. {'name': 'Oaken', 'age': 46},
  188. {'name': 'Hans', 'age': 20},
  189. {'name': 'Sven'},
  190. {'name': 'Olaf', 'age': 4},
  191. {'name': 'Anna', 'age': 17},
  192. ]
  193. ui.table(columns=columns, rows=rows, pagination=3)
  194. ui.table(columns=columns, rows=rows, pagination={'rowsPerPage': 4, 'sortBy': 'age', 'page': 2})
  195. @doc.demo('Handle pagination changes', '''
  196. You can handle pagination changes using the `on_pagination_change` parameter.
  197. ''')
  198. def handle_pagination_changes() -> None:
  199. ui.table(
  200. columns=[{'id': 'Name', 'label': 'Name', 'field': 'Name', 'align': 'left'}],
  201. rows=[{'Name': f'Person {i}'} for i in range(100)],
  202. pagination=3,
  203. on_pagination_change=lambda e: ui.notify(e.value),
  204. )
  205. @doc.demo('Computed props', '''
  206. You can access the computed props of a table within async callback functions.
  207. ''')
  208. def computed_props():
  209. async def show_filtered_sorted_rows():
  210. ui.notify(await table.get_filtered_sorted_rows())
  211. async def show_computed_rows():
  212. ui.notify(await table.get_computed_rows())
  213. table = ui.table(
  214. columns=[
  215. {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left', 'sortable': True},
  216. {'name': 'age', 'label': 'Age', 'field': 'age', 'align': 'left', 'sortable': True}
  217. ],
  218. rows=[
  219. {'name': 'Noah', 'age': 33},
  220. {'name': 'Emma', 'age': 21},
  221. {'name': 'Rose', 'age': 88},
  222. {'name': 'James', 'age': 59},
  223. {'name': 'Olivia', 'age': 62},
  224. {'name': 'Liam', 'age': 18},
  225. ],
  226. row_key='name',
  227. pagination=3,
  228. )
  229. ui.input('Search by name/age').bind_value(table, 'filter')
  230. ui.button('Show filtered/sorted rows', on_click=show_filtered_sorted_rows)
  231. ui.button('Show computed rows', on_click=show_computed_rows)
  232. @doc.demo('Computed fields', '''
  233. You can use functions to compute the value of a column.
  234. The function receives the row as an argument.
  235. See the [Quasar documentation](https://quasar.dev/vue-components/table#defining-the-columns) for more information.
  236. ''')
  237. def computed_fields():
  238. columns = [
  239. {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left'},
  240. {'name': 'length', 'label': 'Length', ':field': 'row => row.name.length'},
  241. ]
  242. rows = [
  243. {'name': 'Alice'},
  244. {'name': 'Bob'},
  245. {'name': 'Christopher'},
  246. ]
  247. ui.table(columns=columns, rows=rows, row_key='name')
  248. @doc.demo('Conditional formatting', '''
  249. You can use scoped slots to conditionally format the content of a cell.
  250. See the [Quasar documentation](https://quasar.dev/vue-components/table#example--body-cell-slot)
  251. for more information about body-cell slots.
  252. In this demo we use a `q-badge` to display the age in red if the person is under 21 years old.
  253. We use the `body-cell-age` slot to insert the `q-badge` into the `age` column.
  254. The ":color" attribute of the `q-badge` is set to "red" if the age is under 21, otherwise it is set to "green".
  255. The colon in front of the "color" attribute indicates that the value is a JavaScript expression.
  256. ''')
  257. def conditional_formatting():
  258. columns = [
  259. {'name': 'name', 'label': 'Name', 'field': 'name'},
  260. {'name': 'age', 'label': 'Age', 'field': 'age'},
  261. ]
  262. rows = [
  263. {'name': 'Alice', 'age': 18},
  264. {'name': 'Bob', 'age': 21},
  265. {'name': 'Carol', 'age': 42},
  266. ]
  267. table = ui.table(columns=columns, rows=rows, row_key='name')
  268. table.add_slot('body-cell-age', '''
  269. <q-td key="age" :props="props">
  270. <q-badge :color="props.value < 21 ? 'red' : 'green'">
  271. {{ props.value }}
  272. </q-badge>
  273. </q-td>
  274. ''')
  275. @doc.demo('Table cells with links', '''
  276. Here is a demo of how to insert links into table cells.
  277. We use the `body-cell-link` slot to insert an `<a>` tag into the `link` column.
  278. ''')
  279. def table_cells_with_links():
  280. columns = [
  281. {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left'},
  282. {'name': 'link', 'label': 'Link', 'field': 'link', 'align': 'left'},
  283. ]
  284. rows = [
  285. {'name': 'Google', 'link': 'https://google.com'},
  286. {'name': 'Facebook', 'link': 'https://facebook.com'},
  287. {'name': 'Twitter', 'link': 'https://twitter.com'},
  288. ]
  289. table = ui.table(columns=columns, rows=rows, row_key='name')
  290. table.add_slot('body-cell-link', '''
  291. <q-td :props="props">
  292. <a :href="props.value">{{ props.value }}</a>
  293. </q-td>
  294. ''')
  295. @doc.demo('Table with masonry-like grid', '''
  296. You can use the `grid` prop to display the table as a masonry-like grid.
  297. See the [Quasar documentation](https://quasar.dev/vue-components/table#grid-style) for more information.
  298. ''')
  299. def table_with_masonry_like_grid():
  300. columns = [
  301. {'name': 'name', 'label': 'Name', 'field': 'name'},
  302. {'name': 'age', 'label': 'Age', 'field': 'age'},
  303. ]
  304. rows = [
  305. {'name': 'Alice', 'age': 18},
  306. {'name': 'Bob', 'age': 21},
  307. {'name': 'Carol', 'age': 42},
  308. ]
  309. table = ui.table(columns=columns, rows=rows, row_key='name').props('grid')
  310. table.add_slot('item', r'''
  311. <q-card flat bordered :props="props" class="m-1">
  312. <q-card-section class="text-center">
  313. <strong>{{ props.row.name }}</strong>
  314. </q-card-section>
  315. <q-separator />
  316. <q-card-section class="text-center">
  317. <div>{{ props.row.age }} years</div>
  318. </q-card-section>
  319. </q-card>
  320. ''')
  321. doc.reference(ui.table)