tosijs-ui 1.5.3 → 1.5.5

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.
@@ -6,6 +6,7 @@ export interface ColumnOptions {
6
6
  width: number;
7
7
  visible?: boolean;
8
8
  align?: string;
9
+ pinned?: 'left' | 'right';
9
10
  sort?: false | 'ascending' | 'descending';
10
11
  headerCell?: (options: ColumnOptions) => HTMLElement;
11
12
  dataCell?: (options: ColumnOptions) => HTMLElement;
@@ -117,8 +118,6 @@ export declare class TosiTable extends WebComponent {
117
118
  multiple: boolean;
118
119
  pinnedTop: number;
119
120
  pinnedBottom: number;
120
- pinnedLeft: number;
121
- pinnedRight: number;
122
121
  nosort: boolean;
123
122
  nohide: boolean;
124
123
  noreorder: boolean;
@@ -147,6 +146,14 @@ export declare class TosiTable extends WebComponent {
147
146
  get columns(): ColumnOptions[];
148
147
  set columns(newColumns: ColumnOptions[]);
149
148
  get visibleColumns(): ColumnOptions[];
149
+ /** @deprecated Set pinned: 'left' on individual columns instead */
150
+ get pinnedLeft(): number;
151
+ /** @deprecated Set pinned: 'left' on individual columns instead */
152
+ set pinnedLeft(n: number);
153
+ /** @deprecated Set pinned: 'right' on individual columns instead */
154
+ get pinnedRight(): number;
155
+ /** @deprecated Set pinned: 'right' on individual columns instead */
156
+ set pinnedRight(n: number);
150
157
  content: null;
151
158
  private computeStickyInfo;
152
159
  private cellClasses;
@@ -164,6 +164,7 @@ export interface ColumnOptions {
164
164
  width: number
165
165
  visible?: boolean
166
166
  align?: string
167
+ pinned?: 'left' | 'right'
167
168
  sort?: false | 'ascending' | 'descending'
168
169
  headerCell?: (options: ColumnOptions) => HTMLElement
169
170
  dataCell?: (options: ColumnOptions) => HTMLElement
@@ -172,11 +173,16 @@ export interface ColumnOptions {
172
173
 
173
174
  ## Pinned Columns and Rows
174
175
 
175
- Set `pinnedLeft` and `pinnedRight` on the table to pin the first/last N
176
- visible columns during horizontal scroll. Set `pinnedTop` and `pinnedBottom`
177
- to pin the first/last N data rows (pinned top rows appear below the
178
- header row). All pinning uses CSS `position: sticky` for frame-perfect
179
- rendering with no jitter.
176
+ Set `pinned: 'left'` or `pinned: 'right'` on individual columns to pin
177
+ them during horizontal scroll. Pinned columns are sorted to the edges
178
+ automatically. You can also pin/unpin columns via the header menu, or by
179
+ dragging a column into/out of a pinned zone.
180
+
181
+ Set `pinnedTop` and `pinnedBottom` to pin the first/last N data rows
182
+ (pinned top rows appear below the header row).
183
+
184
+ All pinning uses CSS `position: sticky` for frame-perfect rendering with
185
+ no jitter.
180
186
 
181
187
  ```js
182
188
  import { elements } from 'tosijs'
@@ -230,24 +236,22 @@ const table = tosiTable({
230
236
  array: rows,
231
237
  rowHeight: 32,
232
238
  pinnedBottom: 1,
233
- pinnedLeft: 2,
234
- pinnedRight: 1,
235
239
  rowRendered(item, cells) {
236
240
  const total = numKeys.reduce((sum, key) => sum + (item[key] || 0), 0)
237
- const cls = total < 0 ? 'row-negative' : ''
238
241
  for (const c of cells) {
239
242
  c.classList.toggle('row-negative', total < 0)
240
243
  }
241
244
  },
242
245
  columns: [
243
- { prop: 'id', name: '#', width: 50, align: 'right' },
244
- { prop: 'name', width: 120 },
246
+ { prop: 'id', name: '#', width: 50, align: 'right', pinned: 'left' },
247
+ { prop: 'name', width: 120, pinned: 'left' },
245
248
  ...dataColumns,
246
249
  {
247
250
  prop: '_actions',
248
251
  name: '',
249
252
  width: 48,
250
253
  sort: false,
254
+ pinned: 'right',
251
255
  dataCell() {
252
256
  return button(
253
257
  {
@@ -403,6 +407,10 @@ You'll need to make sure your localized strings include:
403
407
  - Column
404
408
  - Ascending
405
409
  - Descending
410
+ - Pin
411
+ - Unpin
412
+ - Left
413
+ - Right
406
414
 
407
415
  As well as any column names you want localized.
408
416
  */
@@ -541,8 +549,6 @@ export class TosiTable extends WebComponent {
541
549
  multiple: false,
542
550
  pinnedTop: 0,
543
551
  pinnedBottom: 0,
544
- pinnedLeft: 0,
545
- pinnedRight: 0,
546
552
  nosort: false,
547
553
  nohide: false,
548
554
  noreorder: false,
@@ -650,26 +656,73 @@ export class TosiTable extends WebComponent {
650
656
  this.queueRender();
651
657
  }
652
658
  get visibleColumns() {
653
- return this.columns.filter((c) => c.visible !== false);
659
+ const visible = this.columns.filter((c) => c.visible !== false);
660
+ const left = visible.filter((c) => c.pinned === 'left');
661
+ const middle = visible.filter((c) => !c.pinned);
662
+ const right = visible.filter((c) => c.pinned === 'right');
663
+ return [...left, ...middle, ...right];
664
+ }
665
+ /** @deprecated Set pinned: 'left' on individual columns instead */
666
+ get pinnedLeft() {
667
+ return this.visibleColumns.filter((c) => c.pinned === 'left').length;
668
+ }
669
+ /** @deprecated Set pinned: 'left' on individual columns instead */
670
+ set pinnedLeft(n) {
671
+ const visible = this.columns.filter((c) => c.visible !== false);
672
+ for (const col of visible) {
673
+ if (col.pinned === 'left')
674
+ delete col.pinned;
675
+ }
676
+ for (let i = 0; i < n && i < visible.length; i++) {
677
+ visible[i].pinned = 'left';
678
+ }
679
+ this.queueRender();
680
+ }
681
+ /** @deprecated Set pinned: 'right' on individual columns instead */
682
+ get pinnedRight() {
683
+ return this.visibleColumns.filter((c) => c.pinned === 'right').length;
684
+ }
685
+ /** @deprecated Set pinned: 'right' on individual columns instead */
686
+ set pinnedRight(n) {
687
+ const visible = this.columns.filter((c) => c.visible !== false);
688
+ for (const col of visible) {
689
+ if (col.pinned === 'right')
690
+ delete col.pinned;
691
+ }
692
+ for (let i = visible.length - n; i < visible.length; i++) {
693
+ if (i >= 0)
694
+ visible[i].pinned = 'right';
695
+ }
696
+ this.queueRender();
654
697
  }
655
698
  content = null;
656
699
  computeStickyInfo(cols) {
657
700
  const info = cols.map(() => ({}));
701
+ // Left-pinned columns
658
702
  let leftOffset = 0;
659
- for (let i = 0; i < this.pinnedLeft && i < cols.length; i++) {
703
+ let lastLeft = -1;
704
+ for (let i = 0; i < cols.length; i++) {
705
+ if (cols[i].pinned !== 'left')
706
+ break;
660
707
  info[i].left = leftOffset + 'px';
661
708
  leftOffset += cols[i].width;
662
- if (i === this.pinnedLeft - 1) {
663
- info[i].edgeClass = 'col-edge-right';
664
- }
709
+ lastLeft = i;
710
+ }
711
+ if (lastLeft >= 0) {
712
+ info[lastLeft].edgeClass = 'col-edge-right';
665
713
  }
714
+ // Right-pinned columns
666
715
  let rightOffset = 0;
667
- for (let i = cols.length - 1; i >= 0 && i >= cols.length - this.pinnedRight; i--) {
716
+ let firstRight = cols.length;
717
+ for (let i = cols.length - 1; i >= 0; i--) {
718
+ if (cols[i].pinned !== 'right')
719
+ break;
668
720
  info[i].right = rightOffset + 'px';
669
721
  rightOffset += cols[i].width;
670
- if (i === cols.length - this.pinnedRight) {
671
- info[i].edgeClass = 'col-edge-left';
672
- }
722
+ firstRight = i;
723
+ }
724
+ if (firstRight < cols.length) {
725
+ info[firstRight].edgeClass = 'col-edge-left';
673
726
  }
674
727
  return info;
675
728
  }
@@ -742,10 +795,10 @@ export class TosiTable extends WebComponent {
742
795
  return false;
743
796
  boundaryX += options.width;
744
797
  let visualBoundary;
745
- if (i < this.pinnedLeft) {
798
+ if (options.pinned === 'left') {
746
799
  visualBoundary = boundaryX;
747
800
  }
748
- else if (i >= cols.length - this.pinnedRight) {
801
+ else if (options.pinned === 'right') {
749
802
  visualBoundary = boundaryX - scrollLeft - rightScroll;
750
803
  }
751
804
  else {
@@ -1136,6 +1189,42 @@ export class TosiTable extends WebComponent {
1136
1189
  }),
1137
1190
  });
1138
1191
  }
1192
+ if (menu.length) {
1193
+ menu.push(null);
1194
+ }
1195
+ menu.push({
1196
+ caption: this.localized ? localize('Pin') : 'Pin',
1197
+ icon: 'lock',
1198
+ menuItems: [
1199
+ {
1200
+ caption: this.localized ? localize('Left') : 'Left',
1201
+ icon: 'arrowLeft',
1202
+ enabled: () => options.pinned !== 'left',
1203
+ action() {
1204
+ options.pinned = 'left';
1205
+ queueRender();
1206
+ },
1207
+ },
1208
+ {
1209
+ caption: this.localized ? localize('Right') : 'Right',
1210
+ icon: 'arrowRight',
1211
+ enabled: () => options.pinned !== 'right',
1212
+ action() {
1213
+ options.pinned = 'right';
1214
+ queueRender();
1215
+ },
1216
+ },
1217
+ {
1218
+ caption: this.localized ? localize('Unpin') : 'Unpin',
1219
+ icon: 'unlock',
1220
+ enabled: () => !!options.pinned,
1221
+ action() {
1222
+ delete options.pinned;
1223
+ queueRender();
1224
+ },
1225
+ },
1226
+ ],
1227
+ });
1139
1228
  popMenu({
1140
1229
  target,
1141
1230
  localized: this.localized,
@@ -1175,6 +1264,8 @@ export class TosiTable extends WebComponent {
1175
1264
  const dropped = this.visibleColumns[colIndex];
1176
1265
  const draggedIndex = this.columns.indexOf(this.draggedColumn);
1177
1266
  const droppedIndex = this.columns.indexOf(dropped);
1267
+ // Inherit pinning from the drop target's zone
1268
+ this.draggedColumn.pinned = dropped.pinned;
1178
1269
  this.columns.splice(draggedIndex, 1);
1179
1270
  this.columns.splice(droppedIndex, 0, this.draggedColumn);
1180
1271
  this.queueRender();