@bexis2/bexis2-core-ui 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # bexis-core-ui
2
2
 
3
+ ## 0.3.4
4
+
5
+ - Page
6
+ - add automatic scroll to top button next to help
7
+
8
+ ## 0.3.3
9
+
10
+ - Table
11
+ - `rowHeight (number)`: fixed height for rows in pixels. (if combined with resizability, acts as minimum height)
12
+ - `exportability (boolean)`: if toggled true, lets user to export the current state of the table as CSV.
13
+ - Optimized styles for dark mode.
14
+ - Bug fix related to empty tables.
15
+ - Migrated to new TableOfContents (Beta) on documentation page.
16
+ - LightSwitch for toggling dark mode on documentation pages.
17
+ - Optimized surface colors in dark mode.
18
+
3
19
  ## 0.3.1
4
20
 
5
21
  - add bexis2theme
@@ -5,7 +5,8 @@ import {
5
5
  addPagination,
6
6
  addExpandedRows,
7
7
  addColumnFilters,
8
- addTableFilter
8
+ addTableFilter,
9
+ addDataExport
9
10
  } from "svelte-headless-table/plugins";
10
11
  import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
11
12
  import { SlideToggle, storePopup } from "@skeletonlabs/skeleton";
@@ -16,15 +17,29 @@ import { columnFilter, searchFilter } from "./filter";
16
17
  export let config;
17
18
  let {
18
19
  id: tableId,
20
+ // Unique table ID
19
21
  data,
22
+ // Data store
20
23
  columns,
24
+ // Column configuration
21
25
  resizable = "none",
26
+ // Resizability config
22
27
  height = null,
28
+ // Table height
29
+ rowHeight = null,
30
+ // Row height
23
31
  optionsComponent,
32
+ // Custom component to render in the last column
24
33
  defaultPageSize = 10,
34
+ // Default page size - number of rows to display per page
25
35
  toggle = false,
36
+ // Whether to display the fitToScreen toggle
26
37
  pageSizes = [5, 10, 15, 20],
27
- fitToScreen = true
38
+ // Page sizes to display in the pagination component
39
+ fitToScreen = true,
40
+ // Whether to fit the table to the screen,
41
+ exportable = false
42
+ // Whether to display the export button and enable export functionality
28
43
  } = config;
29
44
  const dispatch = createEventDispatcher();
30
45
  const actionDispatcher = (obj) => dispatch("action", obj);
@@ -35,7 +50,8 @@ const table = createTable(data, {
35
50
  }),
36
51
  sort: addSortBy({ disableMultiSort: true }),
37
52
  page: addPagination({ initialPageSize: defaultPageSize }),
38
- expand: addExpandedRows()
53
+ expand: addExpandedRows(),
54
+ export: addDataExport({ format: "csv" })
39
55
  });
40
56
  const allCols = {};
41
57
  $data.forEach((item) => {
@@ -58,20 +74,29 @@ const tableColumns = [
58
74
  if (columns !== void 0 && key in columns) {
59
75
  const {
60
76
  header,
77
+ // Custom header to display
61
78
  colFilterFn,
79
+ // Custom column filter function
62
80
  colFilterComponent,
81
+ // Custom column filter component
63
82
  instructions,
83
+ // Custom instructions for the column cells (sorting, filtering, searching, rendering)
64
84
  disableFiltering = false,
85
+ // Whether to disable filtering for the column
65
86
  disableSorting = false
87
+ // Whether to disable sorting for the column
66
88
  } = columns[key];
67
89
  const { toSortableValueFn, toFilterableValueFn, toStringFn, renderComponent } = instructions ?? {};
68
90
  return table.column({
91
+ // If header is not provided, use the key as the header
69
92
  header: header ?? key,
70
93
  accessor,
94
+ // Render the cell with the provided component, or use the toStringFn if provided, or just use the value
71
95
  cell: ({ value, row }) => {
72
96
  return renderComponent ? createRender(renderComponent, { value, row }) : toStringFn ? toStringFn(value) : value;
73
97
  },
74
98
  plugins: {
99
+ // Sorting config
75
100
  sort: {
76
101
  disable: disableSorting,
77
102
  invert: true,
@@ -95,6 +120,7 @@ const tableColumns = [
95
120
  }
96
121
  } : void 0,
97
122
  tableFilter: {
123
+ // Search filter config
98
124
  getFilterValue: (row) => {
99
125
  return toStringFn ? toStringFn(row) : row;
100
126
  }
@@ -109,9 +135,11 @@ const tableColumns = [
109
135
  return value ? value : "";
110
136
  },
111
137
  plugins: {
138
+ // Sorting enabled by default
112
139
  sort: {
113
140
  invert: true
114
141
  },
142
+ // Filtering enabled by default
115
143
  colFilter: {
116
144
  fn: columnFilter,
117
145
  render: ({ filterValue: filterValue2, values, id }) => createRender(TableFilter, {
@@ -131,6 +159,11 @@ if (optionsComponent !== void 0) {
131
159
  table.display({
132
160
  id: "optionsColumn",
133
161
  header: "",
162
+ plugins: {
163
+ export: {
164
+ exclude: true
165
+ }
166
+ },
134
167
  cell: ({ row }, _) => {
135
168
  return createRender(optionsComponent, {
136
169
  row: row.isData() ? row.original : null,
@@ -143,6 +176,7 @@ if (optionsComponent !== void 0) {
143
176
  const createdTableColumns = table.createColumns(tableColumns);
144
177
  const { headerRows, pageRows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(createdTableColumns);
145
178
  const { filterValue } = pluginStates.tableFilter;
179
+ const { exportedData } = pluginStates.export;
146
180
  const minWidth = (id) => {
147
181
  if (columns && id in columns) {
148
182
  return columns[id].minWidth ?? 0;
@@ -183,10 +217,20 @@ const resetResize = () => {
183
217
  });
184
218
  }
185
219
  };
220
+ const exportAsCsv = () => {
221
+ const anchor = document.createElement("a");
222
+ anchor.style.display = "none";
223
+ anchor.href = `data:text/csv;charset=utf-8,${encodeURIComponent($exportedData)}`;
224
+ anchor.download = `${tableId}.csv`;
225
+ document.body.appendChild(anchor);
226
+ anchor.click();
227
+ document.body.removeChild(anchor);
228
+ };
186
229
  </script>
187
230
 
188
231
  <div class="grid gap-2 overflow-auto" class:w-fit={!fitToScreen} class:w-full={fitToScreen}>
189
232
  <div class="table-container">
233
+ <!-- Enable the search filter if table is not empty -->
190
234
  {#if $data.length > 0}
191
235
  <input
192
236
  class="input p-2 border border-primary-500"
@@ -195,123 +239,144 @@ const resetResize = () => {
195
239
  placeholder="Search rows..."
196
240
  id="{tableId}-search"
197
241
  />
198
- {/if}
199
-
200
- <div class="flex justify-between items-center py-2 w-full">
201
- <div>
202
- {#if toggle}
203
- <SlideToggle
204
- name="slider-label"
205
- active="bg-primary-500"
206
- size="sm"
207
- checked={fitToScreen}
208
- id="{tableId}-toggle"
209
- on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
210
- >
211
- {/if}
212
- </div>
213
- <div>
214
- {#if resizable !== 'none'}
215
- <button
216
- type="button"
217
- class="btn btn-sm variant-filled-primary rounded-full order-last"
218
- on:click|preventDefault={resetResize}>Reset sizing</button
219
- >
220
- {/if}
242
+ <div class="flex justify-between items-center py-2 w-full">
243
+ <div>
244
+ <!-- Enable the fitToScreen toggle if toggle === true -->
245
+ {#if toggle}
246
+ <SlideToggle
247
+ name="slider-label"
248
+ active="bg-primary-500"
249
+ size="sm"
250
+ checked={fitToScreen}
251
+ id="{tableId}-toggle"
252
+ on:change={() => (fitToScreen = !fitToScreen)}>Fit to screen</SlideToggle
253
+ >
254
+ {/if}
255
+ </div>
256
+ <div class="flex gap-2">
257
+ <!-- Enable the resetResize button if resizable !== 'none' -->
258
+ {#if resizable !== 'none'}
259
+ <button
260
+ type="button"
261
+ class="btn btn-sm variant-filled-primary rounded-full order-last"
262
+ on:click|preventDefault={resetResize}>Reset sizing</button
263
+ >
264
+ {/if}
265
+ {#if exportable}
266
+ <button
267
+ type="button"
268
+ class="btn btn-sm variant-filled-primary rounded-full order-last"
269
+ on:click|preventDefault={exportAsCsv}>Export as CSV</button
270
+ >
271
+ {/if}
272
+ </div>
221
273
  </div>
222
- </div>
274
+ {/if}
223
275
 
224
276
  <div class="overflow-auto" style="height: {height}px">
225
277
  <table
226
278
  {...$tableAttrs}
227
- class="table table-auto table-compact bg-tertiary-500/30 overflow-clip"
279
+ class="table table-auto table-compact bg-tertiary-500/30 dark:bg-tertiary-900/10 overflow-clip"
228
280
  id="{tableId}-table"
229
281
  >
282
+ <!-- If table height is provided, making the top row sticky -->
230
283
  <thead class=" {height != null ? `sticky top-0` : ''}">
231
- {#each $headerRows as headerRow (headerRow.id)}
232
- <Subscribe
233
- rowAttrs={headerRow.attrs()}
234
- let:rowAttrs
235
- rowProps={headerRow.props()}
236
- let:rowProps
237
- >
238
- <tr {...rowAttrs} class="bg-primary-300 items-stretch">
239
- {#each headerRow.cells as cell (cell.id)}
240
- <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
241
- <th
242
- scope="col"
243
- class="!p-2 overflow-auto"
244
- class:resize-x={(resizable === 'columns' || resizable === 'both') &&
245
- !fixedWidth(cell.id)}
246
- {...attrs}
247
- id="th-{tableId}-{cell.id}"
248
- style={cellStyle(cell.id)}
249
- >
250
- <div class="flex justify-between items-center">
251
- <div class="flex gap-1 whitespace-pre-wrap">
252
- <span
253
- class:underline={props.sort.order}
254
- class:normal-case={cell.id !== cell.label}
255
- class:cursor-pointer={!props.sort.disabled}
256
- on:click={props.sort.toggle}
257
- on:keydown={props.sort.toggle}
258
- >
259
- {cell.render()}
260
- </span>
261
- <div class="w-2">
262
- {#if props.sort.order === 'asc'}
263
-
264
- {:else if props.sort.order === 'desc'}
265
-
266
- {/if}
267
- </div>
268
- </div>
269
- {#if cell.isData()}
270
- {#if props.colFilter?.render}
271
- <div class="">
272
- <Render of={props.colFilter.render} />
284
+ {#if $data.length > 0}
285
+ {#each $headerRows as headerRow (headerRow.id)}
286
+ <Subscribe
287
+ rowAttrs={headerRow.attrs()}
288
+ let:rowAttrs
289
+ rowProps={headerRow.props()}
290
+ let:rowProps
291
+ >
292
+ <tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-500 items-stretch">
293
+ {#each headerRow.cells as cell (cell.id)}
294
+ <Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
295
+ <th
296
+ scope="col"
297
+ class="!p-2 overflow-auto"
298
+ class:resize-x={(resizable === 'columns' || resizable === 'both') &&
299
+ !fixedWidth(cell.id)}
300
+ {...attrs}
301
+ id="th-{tableId}-{cell.id}"
302
+ style={cellStyle(cell.id)}
303
+ >
304
+ <div class="flex justify-between items-center">
305
+ <div class="flex gap-1 whitespace-pre-wrap">
306
+ <!-- Adding sorting config and styling -->
307
+ <span
308
+ class:underline={props.sort.order}
309
+ class:normal-case={cell.id !== cell.label}
310
+ class:cursor-pointer={!props.sort.disabled}
311
+ on:click={props.sort.toggle}
312
+ on:keydown={props.sort.toggle}
313
+ >
314
+ {cell.render()}
315
+ </span>
316
+ <div class="w-2">
317
+ {#if props.sort.order === 'asc'}
318
+
319
+ {:else if props.sort.order === 'desc'}
320
+
321
+ {/if}
273
322
  </div>
323
+ </div>
324
+ <!-- Adding column filter config -->
325
+ {#if cell.isData()}
326
+ {#if props.colFilter?.render}
327
+ <div class="">
328
+ <Render of={props.colFilter.render} />
329
+ </div>
330
+ {/if}
274
331
  {/if}
275
- {/if}
276
- </div>
277
- </th>
278
- </Subscribe>
279
- {/each}
280
- </tr>
281
- </Subscribe>
332
+ </div>
333
+ </th>
334
+ </Subscribe>
335
+ {/each}
336
+ </tr>
337
+ </Subscribe>
338
+ {/each}
282
339
  {:else}
340
+ <!-- Table is empty -->
283
341
  <p class="items-center justify-center flex w-full p-10 italic">Nothing to show here.</p>
284
- {/each}
342
+ {/if}
285
343
  </thead>
286
344
 
287
345
  <tbody class="overflow-auto" {...$tableBodyAttrs}>
288
- {#each $pageRows as row (row.id)}
289
- <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
290
- <tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
291
- {#each row.cells as cell, index (cell?.id)}
292
- <Subscribe attrs={cell.attrs()} let:attrs>
293
- <td
294
- {...attrs}
295
- class="!p-2 overflow-auto {index === 0 &&
296
- (resizable === 'rows' || resizable === 'both')
297
- ? 'resize-y'
298
- : ''}"
299
- id="{tableId}-{cell.id}-{row.id}"
300
- >
301
- <div class="flex items-center h-max overflow-x-auto">
302
- <Render of={cell.render()} />
303
- </div>
304
- </td>
305
- </Subscribe>
306
- {/each}
307
- </tr>
308
- </Subscribe>
309
- {/each}
346
+ {#if $data.length > 0}
347
+ {#each $pageRows as row (row.id)}
348
+ <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
349
+ <tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
350
+ {#each row.cells as cell, index (cell?.id)}
351
+ <Subscribe attrs={cell.attrs()} let:attrs>
352
+ <td
353
+ {...attrs}
354
+ class="!p-2 overflow-auto {index === 0 &&
355
+ (resizable === 'rows' || resizable === 'both')
356
+ ? 'resize-y'
357
+ : ''}"
358
+ id="{tableId}-{cell.id}-{row.id}"
359
+ >
360
+ <!-- Adding config for initial rowHeight, if provided -->
361
+ <div
362
+ class="flex items-center"
363
+ style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
364
+ >
365
+ <div class="grow h-full"><Render of={cell.render()} /></div>
366
+ </div>
367
+ </td>
368
+ </Subscribe>
369
+ {/each}
370
+ </tr>
371
+ </Subscribe>
372
+ {/each}
373
+ {/if}
310
374
  </tbody>
311
375
  </table>
312
376
  </div>
313
377
  </div>
314
378
  {#if $data.length > 0}
379
+ <!-- Adding pagination, if table is not empty -->
315
380
  <TablePagination pageConfig={pluginStates.page} {pageSizes} id={tableId} />
316
381
  {/if}
317
382
  </div>
@@ -129,6 +129,7 @@ type = isDate ? "date" : type;
129
129
  class="btn variant-filled-primary btn-sm"
130
130
  type="button"
131
131
  on:click|preventDefault={() => {
132
+ // Set the defaults when cleared
132
133
  firstOption = 'isequal';
133
134
  firstValue = undefined;
134
135
  secondOption = 'isequal';
@@ -4,9 +4,7 @@ let content = "";
4
4
  $:
5
5
  content;
6
6
  onMount(async () => {
7
- console.log("footer");
8
7
  content = await getFooter();
9
- console.log(content);
10
8
  });
11
9
  </script>
12
10
 
@@ -0,0 +1,25 @@
1
+ <script>
2
+
3
+ import Fa from 'svelte-fa/src/fa.svelte';
4
+ import { faAngleUp } from '@fortawesome/free-solid-svg-icons';
5
+
6
+ export let showAtPixel = 1000;
7
+
8
+ let scrollHeight = 0;
9
+
10
+ const gotoTop = () => {
11
+ window.scrollTo({ top: 0, behavior: 'smooth' })
12
+ };
13
+
14
+ $: showGotoTop = scrollHeight > showAtPixel;
15
+ </script>
16
+
17
+ {#if showGotoTop}
18
+ <button
19
+ id="gotToTop"
20
+ class="chip variant-filled-warning fixed bottom-5 right-20 shadow-md"
21
+ on:click={gotoTop}
22
+ ><Fa icon={faAngleUp}/></button >
23
+ {/if}
24
+
25
+ <svelte:window bind:scrollY={scrollHeight} />
@@ -0,0 +1,23 @@
1
+ /** @typedef {typeof __propDef.props} GoToTopProps */
2
+ /** @typedef {typeof __propDef.events} GoToTopEvents */
3
+ /** @typedef {typeof __propDef.slots} GoToTopSlots */
4
+ export default class GoToTop extends SvelteComponentTyped<{
5
+ showAtPixel?: number | undefined;
6
+ }, {
7
+ [evt: string]: CustomEvent<any>;
8
+ }, {}> {
9
+ }
10
+ export type GoToTopProps = typeof __propDef.props;
11
+ export type GoToTopEvents = typeof __propDef.events;
12
+ export type GoToTopSlots = typeof __propDef.slots;
13
+ import { SvelteComponentTyped } from "svelte";
14
+ declare const __propDef: {
15
+ props: {
16
+ showAtPixel?: number | undefined;
17
+ };
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ };
23
+ export {};
@@ -12,6 +12,7 @@ import { storePopup } from "@skeletonlabs/skeleton";
12
12
  import { breadcrumbStore } from "../../stores/pageStores";
13
13
  storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
14
14
  import Docs from "./Docs.svelte";
15
+ import GoToTop from "./GoToTop.svelte";
15
16
  export let title = "";
16
17
  export let note = "";
17
18
  export let links = [];
@@ -25,8 +26,13 @@ onMount(async () => {
25
26
  breadcrumbStore.clean();
26
27
  breadcrumbStore.addItem({ label: title, link: window.location.pathname });
27
28
  });
29
+ let app;
30
+ function scrollToTop() {
31
+ app.scrollIntoView();
32
+ }
28
33
  </script>
29
34
 
35
+ <div class="app" bind:this={app}>
30
36
  <AppShell>
31
37
  <!--header-->
32
38
  <svelte:fragment slot="header">
@@ -79,6 +85,11 @@ onMount(async () => {
79
85
  {/if}
80
86
  </div>
81
87
 
88
+
89
+
90
+ <GoToTop/>
82
91
  <HelpPopUp active={help} />
83
92
  <Notification />
84
93
  </AppShell>
94
+ </div>
95
+
package/dist/index.d.ts CHANGED
@@ -22,12 +22,12 @@ import type { TableConfig, Columns, Column } from './models/Models';
22
22
  import CodeEditor from './components/CodeEditor/CodeEditor.svelte';
23
23
  import Notification from './components/page/Notification.svelte';
24
24
  import TablePlaceholder from './components/page/TablePlaceholder.svelte';
25
+ import { bexis2theme } from './themes/theme-bexis2';
25
26
  export { Checkbox, CheckboxKVPList, CheckboxList, DateInput, DropdownKVP, MultiSelect, NumberInput, TextArea, TextInput };
26
27
  export { FileInfo, FileIcon, FileUploader };
27
28
  export { Spinner, Page, Alert, Menu, ErrorMessage };
28
29
  export { Api } from './services/Api.js';
29
30
  export { host, username, password, setApiConfig } from './stores/apiStores.js';
30
- export { bexis2theme } from './themes/theme-bexis2';
31
31
  export type { userType, inputType, fileUploaderType, linkType, listItemType, keyValuePairType, fileInfoType, fileReaderInfoType, asciiFileReaderInfoType } from './models/Models.js';
32
32
  export { helpStore } from './stores/pageStores';
33
33
  export type { helpStoreType, helpItemType } from './models/Models';
@@ -40,3 +40,4 @@ export { positionType, pageContentLayoutType, decimalCharacterType, orientationT
40
40
  export { Table, TableFilter, columnFilter, searchFilter };
41
41
  export { CodeEditor };
42
42
  export type { TableConfig, Columns, Column };
43
+ export { bexis2theme };
package/dist/index.js CHANGED
@@ -30,6 +30,8 @@ import CodeEditor from './components/CodeEditor/CodeEditor.svelte';
30
30
  import Notification from './components/page/Notification.svelte';
31
31
  //table placeholder
32
32
  import TablePlaceholder from './components/page/TablePlaceholder.svelte';
33
+ // theme
34
+ import { bexis2theme } from './themes/theme-bexis2';
33
35
  //Form
34
36
  export { Checkbox, CheckboxKVPList, CheckboxList, DateInput, DropdownKVP, MultiSelect, NumberInput, TextArea, TextInput };
35
37
  //File
@@ -39,8 +41,6 @@ export { Spinner, Page, Alert, Menu, ErrorMessage };
39
41
  //Api
40
42
  export { Api } from './services/Api.js';
41
43
  export { host, username, password, setApiConfig } from './stores/apiStores.js';
42
- // theme
43
- export { bexis2theme } from './themes/theme-bexis2';
44
44
  //help
45
45
  export { helpStore } from './stores/pageStores';
46
46
  //notification
@@ -55,3 +55,5 @@ export { positionType, pageContentLayoutType, decimalCharacterType, orientationT
55
55
  export { Table, TableFilter, columnFilter, searchFilter };
56
56
  // CodeEditor
57
57
  export { CodeEditor };
58
+ // theme
59
+ export { bexis2theme };
@@ -87,7 +87,9 @@ export interface TableConfig<T> {
87
87
  toggle?: boolean;
88
88
  fitToScreen?: boolean;
89
89
  height?: null | number;
90
+ rowHeight?: number;
90
91
  columns?: Columns;
92
+ exportable?: boolean;
91
93
  pageSizes?: number[];
92
94
  defaultPageSize?: number;
93
95
  optionsComponent?: typeof SvelteComponent;
@@ -95,5 +95,18 @@ export const bexis2theme = {
95
95
  '--color-surface-700': '149 149 149',
96
96
  '--color-surface-800': '119 119 119',
97
97
  '--color-surface-900': '98 98 98' // #626262
98
+ },
99
+ properties_dark: {
100
+ // surface | #2e2e2e
101
+ '--color-surface-50': '224 224 224',
102
+ '--color-surface-100': '213 213 213',
103
+ '--color-surface-200': '203 203 203',
104
+ '--color-surface-300': '171 171 171',
105
+ '--color-surface-400': '109 109 109',
106
+ '--color-surface-500': '46 46 46',
107
+ '--color-surface-600': '41 41 41',
108
+ '--color-surface-700': '35 35 35',
109
+ '--color-surface-800': '28 28 28',
110
+ '--color-surface-900': '23 23 23' // #171717
98
111
  }
99
112
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bexis2/bexis2-core-ui",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -74,7 +74,7 @@
74
74
  "delay": "^6.0.0",
75
75
  "dotenv": "^16.3.1",
76
76
  "eslint4b-prebuilt": "^6.7.2",
77
- "highlight.js": "^11.8.0",
77
+ "highlight.js": "^11.9.0",
78
78
  "highlightjs-svelte": "^1.0.6",
79
79
  "svelte": "^3.54.0",
80
80
  "svelte-codemirror-editor": "^1.1.0",