@coreui/data-grid 0.1.0-rc.0 → 0.3.0-rc.0

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.
Files changed (166) hide show
  1. package/README.md +9 -6
  2. package/dist/css/data-grid.css +156 -38
  3. package/dist/css/data-grid.css.map +1 -1
  4. package/dist/css/data-grid.min.css +2 -2
  5. package/dist/css/data-grid.min.css.map +1 -1
  6. package/dist/css/data-grid.standalone.css +1066 -0
  7. package/dist/css/data-grid.standalone.css.map +1 -0
  8. package/dist/css/data-grid.standalone.min.css +5 -0
  9. package/dist/css/data-grid.standalone.min.css.map +1 -0
  10. package/dist/js/clipboard.cjs +56 -0
  11. package/dist/js/clipboard.cjs.map +1 -0
  12. package/dist/js/clipboard.mjs +54 -0
  13. package/dist/js/clipboard.mjs.map +1 -0
  14. package/dist/js/csv.cjs +36 -59
  15. package/dist/js/csv.cjs.map +1 -1
  16. package/dist/js/csv.mjs +35 -55
  17. package/dist/js/csv.mjs.map +1 -1
  18. package/dist/js/data-grid.esm.js +14108 -9331
  19. package/dist/js/data-grid.esm.js.map +1 -1
  20. package/dist/js/data-grid.esm.min.js +6 -6
  21. package/dist/js/data-grid.esm.min.js.map +1 -1
  22. package/dist/js/data-grid.js +14408 -9634
  23. package/dist/js/data-grid.js.map +1 -1
  24. package/dist/js/data-grid.lite.esm.js +10507 -0
  25. package/dist/js/data-grid.lite.esm.js.map +1 -0
  26. package/dist/js/data-grid.lite.esm.min.js +8 -0
  27. package/dist/js/data-grid.lite.esm.min.js.map +1 -0
  28. package/dist/js/data-grid.lite.js +10520 -0
  29. package/dist/js/data-grid.lite.js.map +1 -0
  30. package/dist/js/data-grid.lite.min.js +8 -0
  31. package/dist/js/data-grid.lite.min.js.map +1 -0
  32. package/dist/js/data-grid.lite.mjs +10507 -0
  33. package/dist/js/data-grid.lite.mjs.map +1 -0
  34. package/dist/js/data-grid.min.js +6 -6
  35. package/dist/js/data-grid.min.js.map +1 -1
  36. package/dist/js/data-grid.mjs +14108 -9331
  37. package/dist/js/data-grid.mjs.map +1 -1
  38. package/dist/js/direction.cjs +26 -0
  39. package/dist/js/direction.cjs.map +1 -0
  40. package/dist/js/direction.mjs +24 -0
  41. package/dist/js/direction.mjs.map +1 -0
  42. package/dist/js/drag.cjs +467 -0
  43. package/dist/js/drag.cjs.map +1 -0
  44. package/dist/js/drag.mjs +464 -0
  45. package/dist/js/drag.mjs.map +1 -0
  46. package/dist/js/export-table-BdabEbJG.mjs +26 -0
  47. package/dist/js/export-table-BdabEbJG.mjs.map +1 -0
  48. package/dist/js/export-table-D762wrZ1.cjs +55 -0
  49. package/dist/js/export-table-D762wrZ1.cjs.map +1 -0
  50. package/dist/js/filters.cjs +89 -132
  51. package/dist/js/filters.cjs.map +1 -1
  52. package/dist/js/filters.mjs +88 -128
  53. package/dist/js/filters.mjs.map +1 -1
  54. package/dist/js/labels.cjs +65 -70
  55. package/dist/js/labels.cjs.map +1 -1
  56. package/dist/js/labels.mjs +64 -66
  57. package/dist/js/labels.mjs.map +1 -1
  58. package/dist/js/print.cjs +66 -0
  59. package/dist/js/print.cjs.map +1 -0
  60. package/dist/js/print.mjs +64 -0
  61. package/dist/js/print.mjs.map +1 -0
  62. package/dist/js/validate.cjs +43 -0
  63. package/dist/js/validate.cjs.map +1 -0
  64. package/dist/js/validate.mjs +42 -0
  65. package/dist/js/validate.mjs.map +1 -0
  66. package/dist/js/xlsx.cjs +240 -0
  67. package/dist/js/xlsx.cjs.map +1 -0
  68. package/dist/js/xlsx.mjs +238 -0
  69. package/dist/js/xlsx.mjs.map +1 -0
  70. package/dist/types/base-component.d.ts +28 -18
  71. package/dist/types/data-grid.d.ts +26 -13
  72. package/dist/types/dom/data.d.ts +15 -0
  73. package/dist/types/dom/event-handler.d.ts +21 -10
  74. package/dist/types/dom/manipulator.d.ts +15 -8
  75. package/dist/types/engine/active-cell.d.ts +1 -0
  76. package/dist/types/engine/data-source.d.ts +1 -1
  77. package/dist/types/engine/features-lite.d.ts +25 -0
  78. package/dist/types/engine/features.d.ts +35 -0
  79. package/dist/types/engine/table.d.ts +3 -3
  80. package/dist/types/engine/validate.d.mts +1 -0
  81. package/dist/types/engine/validate.d.ts +20 -0
  82. package/dist/types/engine/virtualizer.d.ts +2 -0
  83. package/dist/types/icons.d.ts +6 -4
  84. package/dist/types/index-lite.d.mts +1 -0
  85. package/dist/types/index-lite.d.ts +19 -0
  86. package/dist/types/index.d.ts +11 -2
  87. package/dist/types/types.d.ts +53 -31
  88. package/dist/types/ui/body.d.ts +6 -3
  89. package/dist/types/ui/header.d.ts +3 -2
  90. package/dist/types/ui/loading.d.ts +1 -1
  91. package/dist/types/ui/pinning.d.ts +1 -1
  92. package/dist/types/ui/toolbar.d.ts +10 -2
  93. package/dist/types/util/clipboard.d.mts +1 -0
  94. package/dist/types/util/clipboard.d.ts +29 -0
  95. package/dist/types/util/config.d.ts +27 -10
  96. package/dist/types/util/csv.d.ts +5 -5
  97. package/dist/types/util/direction.d.mts +1 -0
  98. package/dist/types/util/direction.d.ts +17 -0
  99. package/dist/types/util/drag.d.mts +1 -0
  100. package/dist/types/util/drag.d.ts +48 -0
  101. package/dist/types/util/export-table.d.ts +47 -0
  102. package/dist/types/util/index.d.ts +66 -10
  103. package/dist/types/util/print.d.mts +1 -0
  104. package/dist/types/util/print.d.ts +32 -0
  105. package/dist/types/util/sanitizer.d.ts +15 -11
  106. package/dist/types/util/xlsx.d.mts +1 -0
  107. package/dist/types/util/xlsx.d.ts +17 -0
  108. package/dist/types/util/zip.d.ts +11 -0
  109. package/package.json +101 -10
  110. package/scss/_base.scss +13 -0
  111. package/scss/_config.scss +14 -0
  112. package/scss/_functions.scss +1 -0
  113. package/scss/_mixins.scss +1 -0
  114. package/scss/base/_buttons.scss +113 -0
  115. package/scss/base/_forms.scss +157 -0
  116. package/scss/base/_helpers.scss +24 -0
  117. package/scss/base/_pagination.scss +100 -0
  118. package/scss/base/_reboot.scss +93 -0
  119. package/scss/base/_root.scss +85 -0
  120. package/scss/base/_spinner.scss +45 -0
  121. package/scss/base/_table.scss +78 -0
  122. package/scss/components/_data-grid.scss +807 -0
  123. package/scss/data-grid.scss +7 -555
  124. package/scss/data-grid.standalone.scss +16 -0
  125. package/scss/functions/_defaults.scss +25 -0
  126. package/scss/mixins/_tokens.scss +12 -0
  127. package/src/{base-component.js → base-component.ts} +21 -17
  128. package/src/data-grid.ts +523 -201
  129. package/src/dom/{data.js → data.ts} +13 -10
  130. package/src/dom/event-handler.ts +380 -0
  131. package/src/dom/{manipulator.js → manipulator.ts} +11 -11
  132. package/src/engine/active-cell.ts +10 -3
  133. package/src/engine/data-source.ts +7 -3
  134. package/src/engine/features-lite.ts +47 -0
  135. package/src/engine/features.ts +68 -0
  136. package/src/engine/table.ts +86 -45
  137. package/src/engine/validate.ts +64 -0
  138. package/src/engine/virtualizer.ts +17 -1
  139. package/src/icons.ts +11 -5
  140. package/src/index-lite.ts +85 -0
  141. package/src/index.ts +30 -2
  142. package/src/labels.ts +6 -4
  143. package/src/types.ts +87 -32
  144. package/src/ui/body.ts +73 -17
  145. package/src/ui/header.ts +51 -26
  146. package/src/ui/loading.ts +2 -2
  147. package/src/ui/pinning.ts +16 -16
  148. package/src/ui/toolbar.ts +27 -3
  149. package/src/util/clipboard.ts +112 -0
  150. package/src/util/{config.js → config.ts} +25 -11
  151. package/src/util/csv.ts +11 -30
  152. package/src/util/direction.ts +37 -0
  153. package/src/util/drag.ts +832 -0
  154. package/src/util/export-table.ts +86 -0
  155. package/src/util/{index.js → index.ts} +55 -35
  156. package/src/util/print.ts +125 -0
  157. package/src/util/{sanitizer.js → sanitizer.ts} +17 -13
  158. package/src/util/xlsx.ts +183 -0
  159. package/src/util/zip.ts +170 -0
  160. package/src/base-component.d.ts +0 -19
  161. package/src/dom/event-handler.d.ts +0 -11
  162. package/src/dom/event-handler.js +0 -331
  163. package/src/dom/manipulator.d.ts +0 -9
  164. package/src/util/config.d.ts +0 -11
  165. package/src/util/index.d.ts +0 -10
  166. package/src/util/sanitizer.d.ts +0 -11
package/src/ui/pinning.ts CHANGED
@@ -5,16 +5,16 @@
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
- const CLASS_LEFT = 'data-grid-cell-pinned-left'
9
- const CLASS_RIGHT = 'data-grid-cell-pinned-right'
10
- const CLASS_LEFT_LAST = 'data-grid-cell-pinned-left-last'
11
- const CLASS_RIGHT_FIRST = 'data-grid-cell-pinned-right-first'
8
+ const CLASS_START = 'data-grid-cell-pinned-start'
9
+ const CLASS_END = 'data-grid-cell-pinned-end'
10
+ const CLASS_START_LAST = 'data-grid-cell-pinned-start-last'
11
+ const CLASS_END_FIRST = 'data-grid-cell-pinned-end-first'
12
12
 
13
13
  export interface PinnedCellInfo {
14
- side: 'left' | 'right'
14
+ side: 'start' | 'end'
15
15
  offset: number
16
16
  edge: boolean
17
- // A left offset that has to clear the (sticky) selection cell; expressed as a
17
+ // A start offset that has to clear the (sticky) selection cell; expressed as a
18
18
  // CSS calc so the select-cell width stays a single source of truth in CSS.
19
19
  afterSelect: boolean
20
20
  }
@@ -25,29 +25,29 @@ export interface GridPinning {
25
25
  }
26
26
 
27
27
  export const applyPinnedCell = (cell: HTMLTableCellElement, info: PinnedCellInfo | null): void => {
28
- cell.classList.remove(CLASS_LEFT, CLASS_RIGHT, CLASS_LEFT_LAST, CLASS_RIGHT_FIRST)
29
- cell.style.left = ''
30
- cell.style.right = ''
28
+ cell.classList.remove(CLASS_START, CLASS_END, CLASS_START_LAST, CLASS_END_FIRST)
29
+ cell.style.insetInlineStart = ''
30
+ cell.style.insetInlineEnd = ''
31
31
 
32
32
  if (!info) {
33
33
  return
34
34
  }
35
35
 
36
- if (info.side === 'left') {
37
- cell.classList.add(CLASS_LEFT)
36
+ if (info.side === 'start') {
37
+ cell.classList.add(CLASS_START)
38
38
  if (info.edge) {
39
- cell.classList.add(CLASS_LEFT_LAST)
39
+ cell.classList.add(CLASS_START_LAST)
40
40
  }
41
41
 
42
- cell.style.left = info.afterSelect ?
42
+ cell.style.insetInlineStart = info.afterSelect ?
43
43
  `calc(var(--cui-data-grid-select-cell-width) + ${info.offset}px)` :
44
44
  `${info.offset}px`
45
45
  } else {
46
- cell.classList.add(CLASS_RIGHT)
46
+ cell.classList.add(CLASS_END)
47
47
  if (info.edge) {
48
- cell.classList.add(CLASS_RIGHT_FIRST)
48
+ cell.classList.add(CLASS_END_FIRST)
49
49
  }
50
50
 
51
- cell.style.right = `${info.offset}px`
51
+ cell.style.insetInlineEnd = `${info.offset}px`
52
52
  }
53
53
  }
package/src/ui/toolbar.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
- import type { CsvDownloadOptions } from '../util/csv'
8
+ import type { CsvDownloadOptions, CsvTable } from '../util/csv'
9
9
  import type { DataGridLabels, DataGridOptions } from '../types'
10
10
 
11
11
  const CLASS_NAME_TOOLBAR = 'data-grid-toolbar'
@@ -19,10 +19,12 @@ export interface GridToolbarContext {
19
19
  // Undo/redo of edit commits - both buttons start disabled, the grid
20
20
  // toggles them as the history stacks fill.
21
21
  history: boolean
22
+ print: boolean
22
23
  search: boolean
23
24
  icons: {
24
25
  columns: string
25
26
  export: string
27
+ print: string
26
28
  redo: string
27
29
  undo: string
28
30
  }
@@ -32,11 +34,24 @@ export interface GridToolbar {
32
34
  element: HTMLDivElement
33
35
  }
34
36
 
37
+ export type DataGridExporter = (table: CsvTable, options: DataGridExportOptions) => void
38
+
39
+ export interface DataGridExportOptions extends CsvDownloadOptions {
40
+ // Sheet name for spreadsheet writers; the CSV writer ignores it.
41
+ sheetName?: string
42
+ // The writer the toolbar button calls, defaulting to the built-in CSV one.
43
+ // Pass `downloadXlsx` from `@coreui/data-grid/xlsx` for Excel. It is
44
+ // INJECTED, never imported by the grid: a writer only reaches the app
45
+ // bundle when the app asks for it.
46
+ exporter?: DataGridExporter
47
+ }
48
+
35
49
  export interface ResolvedToolbar {
36
50
  columns: boolean
37
51
  export: boolean
38
- exportOptions: CsvDownloadOptions
52
+ exportOptions: DataGridExportOptions
39
53
  history: boolean
54
+ print: boolean
40
55
  search: boolean
41
56
  }
42
57
 
@@ -53,6 +68,7 @@ export const resolveToolbar = (options: DataGridOptions): ResolvedToolbar => {
53
68
  export: true,
54
69
  exportOptions: {},
55
70
  history: options.history,
71
+ print: true,
56
72
  search: true
57
73
  }
58
74
  }
@@ -64,6 +80,7 @@ export const resolveToolbar = (options: DataGridOptions): ResolvedToolbar => {
64
80
  export: Boolean(exportOption),
65
81
  exportOptions: typeof exportOption === 'object' ? exportOption : {},
66
82
  history: (toolbar.history ?? false) && options.history,
83
+ print: toolbar.print ?? false,
67
84
  search: (toolbar.search ?? false) || Boolean(options.globalFilter)
68
85
  }
69
86
  }
@@ -73,6 +90,7 @@ export const resolveToolbar = (options: DataGridOptions): ResolvedToolbar => {
73
90
  export: false,
74
91
  exportOptions: {},
75
92
  history: false,
93
+ print: false,
76
94
  search: Boolean(options.globalFilter)
77
95
  }
78
96
  }
@@ -98,7 +116,7 @@ export const createToolbar = (context: GridToolbarContext): GridToolbar => {
98
116
  const element = document.createElement('div')
99
117
  element.classList.add(CLASS_NAME_TOOLBAR)
100
118
 
101
- if (context.columns || context.export || context.history) {
119
+ if (context.columns || context.export || context.print || context.history) {
102
120
  const actions = document.createElement('div')
103
121
  actions.classList.add(CLASS_NAME_ACTIONS)
104
122
 
@@ -114,6 +132,12 @@ export const createToolbar = (context: GridToolbarContext): GridToolbar => {
114
132
  )
115
133
  }
116
134
 
135
+ if (context.print) {
136
+ actions.append(
137
+ createActionButton(context.icons.print, context.labels.toolbarPrint, 'data-coreui-toolbar-print')
138
+ )
139
+ }
140
+
117
141
  if (context.history) {
118
142
  const undoButton = createActionButton(context.icons.undo, context.labels.toolbarUndo, 'data-coreui-toolbar-undo')
119
143
  const redoButton = createActionButton(context.icons.redo, context.labels.toolbarRedo, 'data-coreui-toolbar-redo')
@@ -0,0 +1,112 @@
1
+ /**
2
+ * --------------------------------------------------------------------------
3
+ * CoreUI PRO data-grid util/clipboard.ts
4
+ * License (https://coreui.io/pro/license/)
5
+ * --------------------------------------------------------------------------
6
+ */
7
+
8
+ import type { DataGridItem } from '../types'
9
+
10
+ // table-core resolves the selection to index rectangles and leaves the text to
11
+ // userland, "since the delimiter, the null representation, and any quoting
12
+ // rules are application decisions". This is that decision, made once for all
13
+ // four editions: TSV, because that is what spreadsheets paste as columns.
14
+
15
+ const DELIMITER = '\t'
16
+ const ROW_SEPARATOR = '\n'
17
+ // Disjoint regions are separated the way a spreadsheet shows a gap.
18
+ const REGION_SEPARATOR = '\n\n'
19
+
20
+ export interface ClipboardBounds {
21
+ maxColumnIndex: number
22
+ maxRowIndex: number
23
+ minColumnIndex: number
24
+ minRowIndex: number
25
+ }
26
+
27
+ export interface ClipboardRow {
28
+ getValue: (columnId: string) => unknown
29
+ original: DataGridItem
30
+ }
31
+
32
+ export interface ClipboardTable {
33
+ getCellSelectionBounds: () => ClipboardBounds[]
34
+ getRowModel: () => { rows: ClipboardRow[] }
35
+ }
36
+
37
+ // The grid's own visible columns, in display order — the same order the
38
+ // selection's column indexes address.
39
+ export interface ClipboardColumn {
40
+ id: string
41
+ formatter?: (value: unknown, item: DataGridItem) => string
42
+ }
43
+
44
+ // A tab or a newline inside a value would break the grid the paste target
45
+ // rebuilds, so a value carrying either is quoted the way TSV consumers expect.
46
+ const escape = (text: string): string =>
47
+ (/[\t\n"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text)
48
+
49
+ export const serializeCellSelection = (table: ClipboardTable, columns: ClipboardColumn[]): string => {
50
+ const { rows } = table.getRowModel()
51
+
52
+ return table
53
+ .getCellSelectionBounds()
54
+ .map(bounds => {
55
+ const lines: string[] = []
56
+ for (let rowIndex = bounds.minRowIndex; rowIndex <= bounds.maxRowIndex; rowIndex++) {
57
+ const row = rows[rowIndex]
58
+ if (!row) {
59
+ continue
60
+ }
61
+
62
+ const cells: string[] = []
63
+ for (let columnIndex = bounds.minColumnIndex; columnIndex <= bounds.maxColumnIndex; columnIndex++) {
64
+ const column = columns[columnIndex]
65
+ if (!column) {
66
+ continue
67
+ }
68
+
69
+ const value = row.getValue(column.id)
70
+ // The formatter is what the cell displays; a copied date should
71
+ // paste as the date the user is looking at, not its raw value.
72
+ cells.push(escape(column.formatter ? column.formatter(value, row.original) : String(value ?? '')))
73
+ }
74
+
75
+ lines.push(cells.join(DELIMITER))
76
+ }
77
+
78
+ return lines.join(ROW_SEPARATOR)
79
+ })
80
+ .filter(region => region !== '')
81
+ .join(REGION_SEPARATOR)
82
+ }
83
+
84
+ export const copyCellSelection = async (table: ClipboardTable, columns: ClipboardColumn[]): Promise<string> => {
85
+ const text = serializeCellSelection(table, columns)
86
+ if (text === '') {
87
+ return text
88
+ }
89
+
90
+ try {
91
+ await navigator.clipboard.writeText(text)
92
+ } catch {
93
+ // Denied permission, an insecure context, or a browser without the async
94
+ // clipboard: fall back to the selection-and-execCommand route, which only
95
+ // works inside the user gesture that asked for the copy — which is exactly
96
+ // where this runs.
97
+ const textarea = document.createElement('textarea')
98
+ textarea.value = text
99
+ textarea.setAttribute('aria-hidden', 'true')
100
+ textarea.style.position = 'fixed'
101
+ textarea.style.opacity = '0'
102
+ document.body.append(textarea)
103
+ textarea.select()
104
+ try {
105
+ document.execCommand('copy')
106
+ } finally {
107
+ textarea.remove()
108
+ }
109
+ }
110
+
111
+ return text
112
+ }
@@ -1,10 +1,10 @@
1
- // Vendored from https://github.com/coreui/coreui-pro/blob/bc4daf1cd79d527bf665d615c0a31f53ab0c3d4e/js/src/util/config.js — sync manually
1
+ // Vendored from https://github.com/coreui/coreui-pro/blob/e9355580e/js/src/util/config.ts — sync manually
2
2
  /**
3
3
  * --------------------------------------------------------------------------
4
- * CoreUI util/config.js
4
+ * CoreUI util/config.ts
5
5
  * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
6
6
  *
7
- * This is a modified version of the Bootstrap's util/config.js
7
+ * This is a modified version of the Bootstrap's util/config.ts
8
8
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
9
9
  * --------------------------------------------------------------------------
10
10
  */
@@ -12,47 +12,60 @@
12
12
  import Manipulator from '../dom/manipulator.js'
13
13
  import { isElement, toType } from './index.js'
14
14
 
15
+ /**
16
+ * Types
17
+ */
18
+
19
+ // Deliberately loose: the config object is assembled from data attributes and
20
+ // user input, and each component narrows it with its own exported config type.
21
+ type ComponentConfig = Record<string, any>
22
+
15
23
  /**
16
24
  * Class definition
17
25
  */
18
26
 
19
27
  class Config {
28
+ // Type `this.constructor` so the static members (Default, DefaultType, NAME)
29
+ // are reachable from instance methods without a cast. Declaration-only, so it
30
+ // emits no runtime code.
31
+ declare ['constructor']: typeof Config
32
+
20
33
  // Getters
21
- static get Default() {
34
+ static get Default(): ComponentConfig {
22
35
  return {}
23
36
  }
24
37
 
25
- static get DefaultType() {
38
+ static get DefaultType(): ComponentConfig {
26
39
  return {}
27
40
  }
28
41
 
29
- static get NAME() {
42
+ static get NAME(): string {
30
43
  throw new Error('You have to implement the static method "NAME", for each component!')
31
44
  }
32
45
 
33
- _getConfig(config) {
46
+ _getConfig(config?: ComponentConfig | null): ComponentConfig {
34
47
  config = this._mergeConfigObj(config)
35
48
  config = this._configAfterMerge(config)
36
49
  this._typeCheckConfig(config)
37
50
  return config
38
51
  }
39
52
 
40
- _configAfterMerge(config) {
53
+ _configAfterMerge(config: ComponentConfig): ComponentConfig {
41
54
  return config
42
55
  }
43
56
 
44
- _mergeConfigObj(config, element) {
57
+ _mergeConfigObj(config?: ComponentConfig | null, element?: Element): ComponentConfig {
45
58
  const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse
46
59
 
47
60
  return {
48
61
  ...this.constructor.Default,
49
62
  ...(typeof jsonConfig === 'object' ? jsonConfig : {}),
50
- ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),
63
+ ...(isElement(element) ? Manipulator.getDataAttributes(element as HTMLElement) : {}),
51
64
  ...(typeof config === 'object' ? config : {})
52
65
  }
53
66
  }
54
67
 
55
- _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
68
+ _typeCheckConfig(config: ComponentConfig, configTypes: ComponentConfig = this.constructor.DefaultType): void {
56
69
  for (const [property, expectedTypes] of Object.entries(configTypes)) {
57
70
  const value = config[property]
58
71
  const valueType = isElement(value) ? 'element' : toType(value)
@@ -67,3 +80,4 @@ class Config {
67
80
  }
68
81
 
69
82
  export default Config
83
+ export type { ComponentConfig }
package/src/util/csv.ts CHANGED
@@ -5,11 +5,12 @@
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
- import type { Table } from '@tanstack/table-core'
9
- import type { DataGridItem } from '../types'
8
+ import {
9
+ columnsFor, formatterFor, headerFor, rowsFor, type ExportScope, type ExportTable
10
+ } from './export-table'
10
11
 
11
12
  export interface CsvOptions {
12
- scope?: 'filtered' | 'all' | 'selected'
13
+ scope?: ExportScope
13
14
  delimiter?: string
14
15
  bom?: boolean
15
16
  sanitize?: boolean
@@ -19,10 +20,7 @@ export interface CsvDownloadOptions extends CsvOptions {
19
20
  filename?: string
20
21
  }
21
22
 
22
- interface CsvColumnMeta {
23
- label?: string
24
- formatter?: (value: unknown, item: DataGridItem) => string
25
- }
23
+ export type CsvTable = ExportTable
26
24
 
27
25
  const escapeField = (value: string, delimiter: string): string =>
28
26
  /["\r\n]/.test(value) || value.includes(delimiter) ? `"${value.replace(/"/g, '""')}"` : value
@@ -32,37 +30,20 @@ const escapeField = (value: string, delimiter: string): string =>
32
30
  const sanitizeField = (value: string): string =>
33
31
  /^[=+\-@\t\r]/.test(value) ? `'${value}` : value
34
32
 
35
- const rowsFor = (table: Table<DataGridItem>, scope: CsvOptions['scope']) => {
36
- if (scope === 'all') {
37
- return table.getCoreRowModel().rows
38
- }
39
-
40
- if (scope === 'selected') {
41
- return table.getSelectedRowModel().rows
42
- }
43
-
44
- return table.getSortedRowModel().rows
45
- }
46
-
47
- export const exportCsv = (table: Table<DataGridItem>, options: CsvOptions = {}): string => {
33
+ export const exportCsv = (table: CsvTable, options: CsvOptions = {}): string => {
48
34
  const delimiter = options.delimiter ?? ','
49
- // Export follows the rendered layout: pinning, column order and visibility.
50
- const columns = [
51
- ...table.getLeftVisibleLeafColumns(),
52
- ...table.getCenterVisibleLeafColumns(),
53
- ...table.getRightVisibleLeafColumns()
54
- ]
35
+ const columns = columnsFor(table)
55
36
 
56
37
  const process = (field: string) => escapeField(options.sanitize ? sanitizeField(field) : field, delimiter)
57
38
  const line = (fields: string[]) => fields.map(process).join(delimiter)
58
39
 
59
- const header = line(columns.map(column => String((column.columnDef.meta as CsvColumnMeta)?.label ?? column.id)))
40
+ const header = line(columns.map(column => headerFor(column)))
60
41
  const body = rowsFor(table, options.scope).map(row =>
61
42
  line(columns.map(column => {
62
43
  const value = row.getValue(column.id)
63
- const meta = column.columnDef.meta as CsvColumnMeta
44
+ const formatter = formatterFor(column)
64
45
  // Coerce: a formatter may return a number/null in plain-JS usage.
65
- return meta?.formatter ? String(meta.formatter(value, row.original) ?? '') : String(value ?? '')
46
+ return formatter ? String(formatter(value, row.original) ?? '') : String(value ?? '')
66
47
  }))
67
48
  )
68
49
 
@@ -70,7 +51,7 @@ export const exportCsv = (table: Table<DataGridItem>, options: CsvOptions = {}):
70
51
  return options.bom ? `\uFEFF${csv}` : csv
71
52
  }
72
53
 
73
- export const downloadCsv = (table: Table<DataGridItem>, options: CsvDownloadOptions = {}): void => {
54
+ export const downloadCsv = (table: CsvTable, options: CsvDownloadOptions = {}): void => {
74
55
  const blob = new Blob([exportCsv(table, options)], { type: 'text/csv;charset=utf-8;' })
75
56
  const url = URL.createObjectURL(blob)
76
57
  const anchor = document.createElement('a')
@@ -0,0 +1,37 @@
1
+ /**
2
+ * --------------------------------------------------------------------------
3
+ * CoreUI PRO data-grid util/direction.ts
4
+ * License (https://coreui.io/pro/license/)
5
+ * --------------------------------------------------------------------------
6
+ */
7
+
8
+ // Read from the element rather than the document: a grid can sit inside an RTL
9
+ // subtree of an LTR page, which is why the vendored isRTL() (it checks
10
+ // documentElement.dir) is not the helper for any of this.
11
+ //
12
+ // Angular prerenders in Node, where there is no getComputedStyle — and no
13
+ // layout to have a direction. LTR is the right answer there: the server-side
14
+ // pass emits logical markup either way, and the client corrects on hydration.
15
+ export const isElementRTL = (element: Element | null | undefined): boolean =>
16
+ typeof getComputedStyle === 'function' && element ?
17
+ getComputedStyle(element).direction === 'rtl' :
18
+ false
19
+
20
+ interface ResizeDirectionTable {
21
+ options: { columnResizeDirection?: 'ltr' | 'rtl' }
22
+ setOptions: (updater: (previous: { columnResizeDirection?: 'ltr' | 'rtl' }) => never) => void
23
+ }
24
+
25
+ // table-core copies the options object at construction, so the direction it
26
+ // captured goes stale the moment an app swaps language on a live grid. A
27
+ // resize is the only place the value is read, and starting one is rare enough
28
+ // to re-read the element here.
29
+ export const syncResizeDirection = (
30
+ table: ResizeDirectionTable,
31
+ element: Element | null | undefined
32
+ ): void => {
33
+ const direction = isElementRTL(element) ? 'rtl' : 'ltr'
34
+ if (table.options.columnResizeDirection !== direction) {
35
+ table.setOptions(previous => ({ ...previous, columnResizeDirection: direction }) as never)
36
+ }
37
+ }