@coreui/data-grid 0.2.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 (82) hide show
  1. package/README.md +4 -2
  2. package/dist/css/data-grid.css +11 -11
  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 +1 -1
  11. package/dist/js/clipboard.mjs +1 -1
  12. package/dist/js/csv.cjs +2 -2
  13. package/dist/js/csv.mjs +2 -2
  14. package/dist/js/data-grid.esm.js +45 -14
  15. package/dist/js/data-grid.esm.js.map +1 -1
  16. package/dist/js/data-grid.esm.min.js +2 -2
  17. package/dist/js/data-grid.esm.min.js.map +1 -1
  18. package/dist/js/data-grid.js +45 -14
  19. package/dist/js/data-grid.js.map +1 -1
  20. package/dist/js/data-grid.lite.esm.js +45 -14
  21. package/dist/js/data-grid.lite.esm.js.map +1 -1
  22. package/dist/js/data-grid.lite.esm.min.js +2 -2
  23. package/dist/js/data-grid.lite.esm.min.js.map +1 -1
  24. package/dist/js/data-grid.lite.js +45 -14
  25. package/dist/js/data-grid.lite.js.map +1 -1
  26. package/dist/js/data-grid.lite.min.js +2 -2
  27. package/dist/js/data-grid.lite.min.js.map +1 -1
  28. package/dist/js/data-grid.lite.mjs +45 -14
  29. package/dist/js/data-grid.lite.mjs.map +1 -1
  30. package/dist/js/data-grid.min.js +2 -2
  31. package/dist/js/data-grid.min.js.map +1 -1
  32. package/dist/js/data-grid.mjs +45 -14
  33. package/dist/js/data-grid.mjs.map +1 -1
  34. package/dist/js/direction.cjs +26 -0
  35. package/dist/js/direction.cjs.map +1 -0
  36. package/dist/js/direction.mjs +24 -0
  37. package/dist/js/direction.mjs.map +1 -0
  38. package/dist/js/drag.cjs +15 -7
  39. package/dist/js/drag.cjs.map +1 -1
  40. package/dist/js/drag.mjs +15 -7
  41. package/dist/js/drag.mjs.map +1 -1
  42. package/dist/js/{export-table-CNAKStzj.mjs → export-table-BdabEbJG.mjs} +2 -2
  43. package/dist/js/{export-table-CNAKStzj.mjs.map → export-table-BdabEbJG.mjs.map} +1 -1
  44. package/dist/js/{export-table-doIwvE5U.cjs → export-table-D762wrZ1.cjs} +2 -2
  45. package/dist/js/{export-table-doIwvE5U.cjs.map → export-table-D762wrZ1.cjs.map} +1 -1
  46. package/dist/js/filters.cjs +1 -1
  47. package/dist/js/filters.mjs +1 -1
  48. package/dist/js/labels.cjs +1 -1
  49. package/dist/js/labels.mjs +1 -1
  50. package/dist/js/print.cjs +1 -1
  51. package/dist/js/print.mjs +1 -1
  52. package/dist/js/validate.cjs +1 -1
  53. package/dist/js/validate.mjs +1 -1
  54. package/dist/js/xlsx.cjs +2 -2
  55. package/dist/js/xlsx.mjs +2 -2
  56. package/dist/types/engine/active-cell.d.ts +1 -0
  57. package/dist/types/engine/table.d.ts +1 -1
  58. package/dist/types/util/direction.d.mts +1 -0
  59. package/dist/types/util/direction.d.ts +17 -0
  60. package/package.json +14 -1
  61. package/scss/_base.scss +13 -0
  62. package/scss/_config.scss +14 -0
  63. package/scss/_functions.scss +1 -0
  64. package/scss/_mixins.scss +1 -0
  65. package/scss/base/_buttons.scss +113 -0
  66. package/scss/base/_forms.scss +157 -0
  67. package/scss/base/_helpers.scss +24 -0
  68. package/scss/base/_pagination.scss +100 -0
  69. package/scss/base/_reboot.scss +93 -0
  70. package/scss/base/_root.scss +85 -0
  71. package/scss/base/_spinner.scss +45 -0
  72. package/scss/base/_table.scss +78 -0
  73. package/scss/components/_data-grid.scss +807 -0
  74. package/scss/data-grid.scss +7 -743
  75. package/scss/data-grid.standalone.scss +16 -0
  76. package/scss/functions/_defaults.scss +25 -0
  77. package/scss/mixins/_tokens.scss +12 -0
  78. package/src/data-grid.ts +18 -3
  79. package/src/engine/active-cell.ts +10 -3
  80. package/src/engine/table.ts +9 -1
  81. package/src/util/direction.ts +37 -0
  82. package/src/util/drag.ts +40 -6
@@ -0,0 +1,16 @@
1
+ /*!
2
+ * CoreUI PRO data-grid.standalone.scss v0.1.0
3
+ * License (https://coreui.io/pro/license/)
4
+ */
5
+
6
+ // The grid plus the base styles it would otherwise borrow from
7
+ // @coreui/coreui(-pro). Load this INSTEAD of data-grid.css when the CoreUI
8
+ // stylesheet is not on the page.
9
+
10
+ // scss-docs-start import-stack
11
+ @forward "config";
12
+ @forward "functions";
13
+ @forward "mixins";
14
+ @forward "base";
15
+ @forward "components/data-grid";
16
+ // scss-docs-end import-stack
@@ -0,0 +1,25 @@
1
+ // Vendored from @coreui/coreui-pro (scss/functions/_defaults.scss, v6 line).
2
+ // Sync manually — do not diverge.
3
+
4
+ @use "sass:map";
5
+ @use "sass:meta";
6
+
7
+ // Merge overrides on top of defaults, stripping null entries.
8
+ // Null values let users remove map keys via @use ... with().
9
+ // Accepts a list as $defaults (converted to a map with `true` values).
10
+ @function defaults($defaults, $overrides) {
11
+ @if meta.type-of($defaults) == "list" {
12
+ $map: ();
13
+ @each $key in $defaults {
14
+ $map: map.merge($map, ($key: true));
15
+ }
16
+ $defaults: $map;
17
+ }
18
+ $merged: map.merge($defaults, $overrides);
19
+ @each $key, $value in $merged {
20
+ @if $value == null {
21
+ $merged: map.remove($merged, $key);
22
+ }
23
+ }
24
+ @return $merged;
25
+ }
@@ -0,0 +1,12 @@
1
+ // Vendored from @coreui/coreui-pro (scss/mixins/_tokens.scss, v6 line).
2
+ // Sync manually — do not diverge.
3
+
4
+ // Output a token map as CSS custom properties
5
+
6
+ // scss-docs-start mixin-tokens
7
+ @mixin tokens($map) {
8
+ @each $prop, $value in $map {
9
+ #{$prop}: #{$value};
10
+ }
11
+ }
12
+ // scss-docs-end mixin-tokens
package/src/data-grid.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  } from './util/drag'
21
21
  import { createScheduler, type Scheduler } from './engine/scheduler'
22
22
  import { createGridTable, subscribeToTableState } from './engine/table'
23
+ import { isElementRTL, syncResizeDirection } from './util/direction'
23
24
  import { createGridVirtualizer, type GridVirtualizer } from './engine/virtualizer'
24
25
  import {
25
26
  DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON, DEFAULT_MOVE_END_ICON, DEFAULT_MOVE_START_ICON,
@@ -134,6 +135,12 @@ const STATE_STORAGE_PREFIX = 'coreui-data-grid:'
134
135
  const STATE_AUTOSAVE_DEBOUNCE_MS = 250
135
136
  const HISTORY_LIMIT = 100
136
137
 
138
+ // Under RTL the two horizontal keys point at the opposite logical neighbour.
139
+ const INVERTED_ARROWS: Record<string, string | undefined> = {
140
+ ArrowLeft: 'ArrowRight',
141
+ ArrowRight: 'ArrowLeft'
142
+ }
143
+
137
144
  const CELL_SELECTION_DIRECTIONS: Record<string, 'up' | 'down' | 'left' | 'right' | undefined> = {
138
145
  ArrowDown: 'down',
139
146
  ArrowLeft: 'left',
@@ -567,7 +574,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
567
574
 
568
575
  private _createTable(): DataGridTable {
569
576
  this._unsubscribeState?.()
570
- const table = createGridTable(this._config)
577
+ const table = createGridTable(this._config, () => this._element)
571
578
  this._unsubscribeState = subscribeToTableState(table, (previous, next) => {
572
579
  this._notifyStateChange(previous, next)
573
580
  this._scheduler.schedule()
@@ -1293,7 +1300,9 @@ private _undoStack: DataGridEditCommitEntry[] = []
1293
1300
  // Shift+arrow grows the range from its anchor and leaves focus put; a
1294
1301
  // plain arrow moves focus and collapses the range to the new cell.
1295
1302
  if (this._config.cellSelection && found.cell.rowIndex >= 0 && result.cell.rowIndex >= 0) {
1296
- const direction = CELL_SELECTION_DIRECTIONS[event.key]
1303
+ const direction = CELL_SELECTION_DIRECTIONS[
1304
+ isElementRTL(this._element) ? (INVERTED_ARROWS[event.key] ?? event.key) : event.key
1305
+ ]
1297
1306
  if (event.shiftKey && direction) {
1298
1307
  this._table.extendCellSelection(direction)
1299
1308
  this._scheduler.schedule()
@@ -1643,7 +1652,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
1643
1652
  pageStep: paginated ?
1644
1653
  this._table.store.state.pagination.pageSize :
1645
1654
  Math.max(1, viewportRows || rowCount),
1646
- paginated
1655
+ paginated,
1656
+ rtl: isElementRTL(this._element)
1647
1657
  }
1648
1658
  }
1649
1659
 
@@ -1975,6 +1985,11 @@ private _undoStack: DataGridEditCommitEntry[] = []
1975
1985
  event.preventDefault()
1976
1986
  }
1977
1987
 
1988
+ // The direction table-core snapshotted at construction may be stale — an
1989
+ // app that swaps language flips dir on a live grid. A resize is the only
1990
+ // moment it matters, and it is rare enough to re-read here.
1991
+ syncResizeDirection(this._table, this._element)
1992
+
1978
1993
  const header = this._table.getHeaderGroups()[0]?.headers.find(item => item.column.id === key)
1979
1994
  header?.getResizeHandler()(event)
1980
1995
  }
@@ -23,6 +23,9 @@ export interface ActiveCellContext {
23
23
  // page size (paginated).
24
24
  pageStep: number
25
25
  paginated: boolean
26
+ // Writing direction of the grid. Arrow keys are physical, column order is
27
+ // logical, so under RTL the two horizontal keys trade places.
28
+ rtl?: boolean
26
29
  }
27
30
 
28
31
  export interface NavigationResult {
@@ -72,7 +75,7 @@ export const resolveNavigation = (
72
75
  current: ActiveCell,
73
76
  context: ActiveCellContext
74
77
  ): NavigationResult | null => {
75
- const { columnIds, rowCount, pageStep, paginated } = context
78
+ const { columnIds, rowCount, pageStep, paginated, rtl } = context
76
79
  const columnIndex = columnIds.indexOf(current.columnId)
77
80
  if (columnIndex === -1 || columnIds.length === 0) {
78
81
  return null
@@ -86,11 +89,15 @@ export const resolveNavigation = (
86
89
 
87
90
  switch (key) {
88
91
  case 'ArrowLeft': {
89
- return cell(current.rowIndex, Math.max(columnIndex - 1, 0))
92
+ return rtl ?
93
+ cell(current.rowIndex, Math.min(columnIndex + 1, lastColumn)) :
94
+ cell(current.rowIndex, Math.max(columnIndex - 1, 0))
90
95
  }
91
96
 
92
97
  case 'ArrowRight': {
93
- return cell(current.rowIndex, Math.min(columnIndex + 1, lastColumn))
98
+ return rtl ?
99
+ cell(current.rowIndex, Math.max(columnIndex - 1, 0)) :
100
+ cell(current.rowIndex, Math.min(columnIndex + 1, lastColumn))
94
101
  }
95
102
 
96
103
  case 'ArrowUp': {
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { constructTable, type FilterFn } from '@tanstack/table-core'
9
9
  import { evaluateColumnFilter } from '../filters'
10
+ import { isElementRTL } from '../util/direction'
10
11
  import type {
11
12
  DataGridColumn, DataGridColumnFilterValue, DataGridFeatures, DataGridItem, DataGridOptions,
12
13
  DataGridTable, DataGridTableState
@@ -27,7 +28,10 @@ const toTanStackFilterFn = (column: DataGridColumn): FilterFn<DataGridFeatures,
27
28
  evaluateColumnFilter(row.getValue(columnId), filterValue as DataGridColumnFilterValue, column.filterType)
28
29
  }
29
30
 
30
- export const createGridTable = (options: DataGridOptions): DataGridTable => {
31
+ export const createGridTable = (
32
+ options: DataGridOptions,
33
+ getElement?: () => Element | null
34
+ ): DataGridTable => {
31
35
  const sortingOptions = typeof options.sorting === 'object' ? options.sorting : {}
32
36
  const sizingOptions = typeof options.columnSizing === 'object' ? options.columnSizing : {}
33
37
  const paginationOptions = typeof options.pagination === 'object' ? options.pagination : {}
@@ -112,6 +116,10 @@ export const createGridTable = (options: DataGridOptions): DataGridTable => {
112
116
  }
113
117
  }),
114
118
  columnResizeMode: sizingOptions.mode ?? 'onChange',
119
+ // Construction-time only — table-core copies the options object, so a
120
+ // getter here would never be re-read. A direction flipped later is picked
121
+ // up by refreshing this through setOptions when a resize starts.
122
+ columnResizeDirection: isElementRTL(getElement?.()) ? 'rtl' : 'ltr',
115
123
  data: serverSide ? [] : options.items,
116
124
  enableCellSelection: Boolean(options.cellSelection),
117
125
  enableColumnFilters: Boolean(options.columnFilters),
@@ -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
+ }
package/src/util/drag.ts CHANGED
@@ -124,6 +124,17 @@ export const createDragController = (options: DragControllerOptions): DragContro
124
124
 
125
125
  let sourceHit: DragHit | null = null
126
126
  let dragging = false
127
+ // +1 while the axis runs the way the coordinates do, -1 under RTL, where the
128
+ // logical column order runs against increasing x. Negating the axis keeps
129
+ // every comparison below — the zone tiling, the hysteresis band, both edge
130
+ // clamps, `index > fromIndex` — written once, for one direction. Only the
131
+ // three places that touch pixels again convert back: the indicator's side,
132
+ // the preview transform and the settling ghost.
133
+ //
134
+ // Read from the grid's own computed direction rather than the document's: a
135
+ // grid can sit in an RTL subtree of an LTR page, which is exactly the case
136
+ // the vendored isRTL() cannot see.
137
+ let axisSign = 1
127
138
  let origin = { x: 0, y: 0 }
128
139
  let pointer = { x: 0, y: 0 }
129
140
  let drop: DragDrop | null = null
@@ -147,6 +158,18 @@ export const createDragController = (options: DragControllerOptions): DragContro
147
158
  const contentStarts = new Map<string, number>()
148
159
  const contentSizes = new Map<string, number>()
149
160
 
161
+ // A rect's logical start: its leading edge along the axis, in the negated
162
+ // space when the axis is negated, so the span stays [start, start + size].
163
+ const logicalStart = (rect: DOMRect): number => {
164
+ if (!horizontal) {
165
+ return rect.top
166
+ }
167
+
168
+ return axisSign === 1 ? rect.left : -rect.right
169
+ }
170
+
171
+ const logicalPointer = (): number => (horizontal ? pointer.x * axisSign : pointer.y)
172
+
150
173
  const stopAutoScroll = () => {
151
174
  if (scrollFrame) {
152
175
  cancelAnimationFrame(scrollFrame)
@@ -212,7 +235,9 @@ export const createDragController = (options: DragControllerOptions): DragContro
212
235
  const rootRect = root.getBoundingClientRect()
213
236
  indicator.hidden = false
214
237
  if (horizontal) {
215
- indicator.style.left = `${after ? rect.right : rect.left}px`
238
+ // `after` is logical; which pixel edge that is flips with the axis.
239
+ const trailing = after === (axisSign === 1)
240
+ indicator.style.left = `${trailing ? rect.right : rect.left}px`
216
241
  indicator.style.top = `${rootRect.top}px`
217
242
  indicator.style.height = `${rootRect.height}px`
218
243
  } else {
@@ -224,6 +249,7 @@ export const createDragController = (options: DragControllerOptions): DragContro
224
249
 
225
250
  const startDrag = () => {
226
251
  dragging = true
252
+ axisSign = horizontal && getComputedStyle(root).direction === 'rtl' ? -1 : 1
227
253
  // Chrome focuses a button on mousedown (a press on a header lands on the
228
254
  // sort button), and tearing a FOCUSED element out of the DOM — which the
229
255
  // drop's header re-append does — makes the browser reset the scroller it
@@ -284,7 +310,11 @@ export const createDragController = (options: DragControllerOptions): DragContro
284
310
  }
285
311
 
286
312
  const rect = scrollElement.getBoundingClientRect()
287
- return horizontal ? rect.left - scrollElement.scrollLeft : rect.top - scrollElement.scrollTop
313
+ // scrollLeft stays a physical axis in every engine (RTL just runs it
314
+ // negative), so it enters the negated space through the sign.
315
+ return horizontal ?
316
+ logicalStart(rect) - (axisSign * scrollElement.scrollLeft) :
317
+ rect.top - scrollElement.scrollTop
288
318
  }
289
319
 
290
320
  const captureItem = (id: string, origin: number): boolean => {
@@ -296,7 +326,7 @@ export const createDragController = (options: DragControllerOptions): DragContro
296
326
  const rect = element.getBoundingClientRect()
297
327
  // Corrected by the applied delta for items first seen mid-preview (a row
298
328
  // revealed by auto-scroll); at drag start every delta is zero.
299
- const start = (horizontal ? rect.left : rect.top) - (previewDeltas.get(id) ?? 0)
329
+ const start = logicalStart(rect) - (previewDeltas.get(id) ?? 0)
300
330
  contentStarts.set(id, start - origin)
301
331
  contentSizes.set(id, horizontal ? rect.width : rect.height)
302
332
  return true
@@ -312,7 +342,7 @@ export const createDragController = (options: DragControllerOptions): DragContro
312
342
  }
313
343
 
314
344
  const updateTarget = () => {
315
- const position = horizontal ? pointer.x : pointer.y
345
+ const position = logicalPointer()
316
346
  const origin = contentOrigin()
317
347
 
318
348
  const zones: Array<{ id: string; start: number; size: number }> = []
@@ -477,7 +507,9 @@ export const createDragController = (options: DragControllerOptions): DragContro
477
507
  }
478
508
 
479
509
  for (const element of options.items.getElements(id)) {
480
- element.style.transform = horizontal ? `translateX(${delta}px)` : `translateY(${delta}px)`
510
+ element.style.transform = horizontal ?
511
+ `translateX(${axisSign * delta}px)` :
512
+ `translateY(${delta}px)`
481
513
  element.classList.add(CLASS_NAME_PREVIEW)
482
514
  }
483
515
 
@@ -531,7 +563,9 @@ export const createDragController = (options: DragControllerOptions): DragContro
531
563
  }
532
564
 
533
565
  const delta = previewDeltas.get(sourceHit.id) ?? 0
534
- return horizontal ? { x: rect.left - delta, y: rect.top } : { x: rect.left, y: rect.top - delta }
566
+ return horizontal ?
567
+ { x: rect.left - (axisSign * delta), y: rect.top } :
568
+ { x: rect.left, y: rect.top - delta }
535
569
  }
536
570
 
537
571
  const cleanup = () => {