yummy-guide-generic-administrate 0.8.0 → 0.8.1
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/yummy_guide_administrate/column_resizer.js +257 -45
- data/app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js +35 -0
- data/app/assets/javascripts/yummy_guide_administrate/sticky_table_headers.js +152 -0
- data/app/assets/stylesheets/yummy_guide_administrate/_column_resizer.scss +33 -4
- data/lib/yummy_guide/administrate/version.rb +1 -1
- data/spec/yummy_guide/administrate/column_resizer_asset_spec.rb +124 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0c5692ebb60bfcc4d51157cdca7e52f974523f546c0d75ab5e3b350815a5316
|
|
4
|
+
data.tar.gz: 9f398e159619da8c35955fcaaa0a2d2c7a1df5249154486c95326a5fe99e6b1d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92ea6916e4083ad8dc0301f48df920d11f63a7f028846fbbdd6d9886b7b29eb30f0e812e2fa413b8db23de600050460867ec9a93d0d799124032619cc552b3fb
|
|
7
|
+
data.tar.gz: 688558efd455e8a364b0a7a070e2f088fdd4adb6684a4b143e37438cd8a5d8192f49918d834afba7478851b075c1984570e5d6666bdd04850f229167102a03fe
|
|
@@ -4,21 +4,24 @@
|
|
|
4
4
|
var HEADER_CLASS = 'admin-column-resizer__header';
|
|
5
5
|
var TABLE_CLASS = 'admin-column-resizer__table';
|
|
6
6
|
var DRAGGING_BODY_CLASS = 'admin-column-resizer--dragging';
|
|
7
|
+
var APPLYING_BODY_CLASS = 'admin-column-resizer--applying';
|
|
8
|
+
var PREVIEW_CLASS = 'admin-column-resizer__preview';
|
|
7
9
|
var FIXED_HEADER_TABLE_CLASS = 'table-fixed-header__table';
|
|
10
|
+
var ADJUSTED_COLUMNS_ATTRIBUTE = 'data-admin-column-resizer-adjusted-columns';
|
|
8
11
|
var STORAGE_PREFIX = 'yummyGuideAdminColumnWidths:v1:';
|
|
9
12
|
var STYLE_ELEMENT_ID = 'admin-column-resizer-rules';
|
|
10
13
|
var WIDTH_VAR_PREFIX = '--admin-column-resizer-col-';
|
|
11
14
|
var MIN_WIDTH = 48;
|
|
12
|
-
var STICKY_REFRESH_INTERVAL = 120;
|
|
13
15
|
|
|
14
16
|
var dragState = null;
|
|
15
|
-
var
|
|
17
|
+
var dragPreviewFrame = null;
|
|
18
|
+
var widthApplyFrame = null;
|
|
16
19
|
var generatedRuleCount = 0;
|
|
17
20
|
var initializedHandles = new WeakSet();
|
|
18
21
|
var tableStates = new WeakMap();
|
|
19
22
|
var stickyRefreshFrame = null;
|
|
20
|
-
var
|
|
21
|
-
var
|
|
23
|
+
var stickyRefreshCallbacks = [];
|
|
24
|
+
var applyingWidth = false;
|
|
22
25
|
|
|
23
26
|
function storageScopeForTable(table) {
|
|
24
27
|
var sourceTable = matchingSourceTable(table);
|
|
@@ -74,6 +77,10 @@
|
|
|
74
77
|
return preciseNumber(rectWidth || element.offsetWidth || 0);
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
function viewportHeight() {
|
|
81
|
+
return window.innerHeight || document.documentElement.clientHeight || 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
function normalizedIdentifier(value) {
|
|
78
85
|
return (value || '').toString().trim().toLowerCase()
|
|
79
86
|
.replace(/[^a-z0-9]+/g, '_')
|
|
@@ -169,13 +176,22 @@
|
|
|
169
176
|
function columnRule(index) {
|
|
170
177
|
var nthChild = index + 1;
|
|
171
178
|
var variableName = columnWidthVariable(index);
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
var adjustedTableSelector = '.' + TABLE_CLASS + '[' + ADJUSTED_COLUMNS_ATTRIBUTE + '~="' + nthChild + '"]';
|
|
180
|
+
var selectors = [
|
|
181
|
+
adjustedTableSelector + ' > thead > tr > :nth-child(' + nthChild + ')',
|
|
182
|
+
adjustedTableSelector + ' > tbody > tr > :nth-child(' + nthChild + ')',
|
|
183
|
+
adjustedTableSelector + ' > tfoot > tr > :nth-child(' + nthChild + ')'
|
|
184
|
+
];
|
|
185
|
+
var selector = selectors.join(', ');
|
|
186
|
+
|
|
187
|
+
var contentSelector = selectors.concat(selectors.map(function(cellSelector) {
|
|
188
|
+
return cellSelector + ' *';
|
|
189
|
+
})).join(', ');
|
|
177
190
|
|
|
178
|
-
return
|
|
191
|
+
return [
|
|
192
|
+
selector + ' { box-sizing: border-box !important; width: var(' + variableName + ') !important; min-width: var(' + variableName + ') !important; max-width: var(' + variableName + ') !important; }',
|
|
193
|
+
contentSelector + ' { white-space: normal !important; overflow-wrap: anywhere !important; word-break: break-word !important; }'
|
|
194
|
+
].join('\n');
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
function ensureStyleElement() {
|
|
@@ -315,6 +331,33 @@
|
|
|
315
331
|
colgroup.children[index].style.width = widthValue;
|
|
316
332
|
}
|
|
317
333
|
|
|
334
|
+
function adjustedColumnIndexes(table) {
|
|
335
|
+
return (table.getAttribute(ADJUSTED_COLUMNS_ATTRIBUTE) || '')
|
|
336
|
+
.split(/\s+/)
|
|
337
|
+
.filter(Boolean);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function setAdjustedColumn(table, index, adjusted) {
|
|
341
|
+
var indexToken = (index + 1).toString();
|
|
342
|
+
var indexes = adjustedColumnIndexes(table).filter(function(candidate, candidateIndex, candidates) {
|
|
343
|
+
return candidate !== indexToken && candidates.indexOf(candidate) === candidateIndex;
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
if (adjusted) {
|
|
347
|
+
indexes.push(indexToken);
|
|
348
|
+
indexes.sort(function(left, right) {
|
|
349
|
+
return parseInt(left, 10) - parseInt(right, 10);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (indexes.length === 0) {
|
|
354
|
+
table.removeAttribute(ADJUSTED_COLUMNS_ATTRIBUTE);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
table.setAttribute(ADJUSTED_COLUMNS_ATTRIBUTE, indexes.join(' '));
|
|
359
|
+
}
|
|
360
|
+
|
|
318
361
|
function clearColgroupWidth(table, columnCount, index) {
|
|
319
362
|
var colgroup = ensureManagedColgroup(table, columnCount);
|
|
320
363
|
if (!colgroup || !colgroup.children[index]) return;
|
|
@@ -329,6 +372,7 @@
|
|
|
329
372
|
|
|
330
373
|
var widthValue = cssPixelValue(width);
|
|
331
374
|
table.style.setProperty(columnWidthVariable(index), widthValue);
|
|
375
|
+
setAdjustedColumn(table, index, true);
|
|
332
376
|
applyColgroupWidth(table, state.columnCount, index, widthValue);
|
|
333
377
|
}
|
|
334
378
|
|
|
@@ -338,6 +382,7 @@
|
|
|
338
382
|
if (!state || index === undefined) return;
|
|
339
383
|
|
|
340
384
|
table.style.removeProperty(columnWidthVariable(index));
|
|
385
|
+
setAdjustedColumn(table, index, false);
|
|
341
386
|
clearColgroupWidth(table, state.columnCount, index);
|
|
342
387
|
}
|
|
343
388
|
|
|
@@ -381,26 +426,23 @@
|
|
|
381
426
|
|
|
382
427
|
function dispatchStickyRefresh() {
|
|
383
428
|
stickyRefreshFrame = null;
|
|
384
|
-
lastStickyRefreshAt = Date.now();
|
|
385
429
|
window.dispatchEvent(new Event('resize'));
|
|
386
|
-
}
|
|
387
430
|
|
|
388
|
-
|
|
389
|
-
|
|
431
|
+
var callbacks = stickyRefreshCallbacks;
|
|
432
|
+
stickyRefreshCallbacks = [];
|
|
433
|
+
callbacks.forEach(function(callback) {
|
|
434
|
+
callback();
|
|
435
|
+
});
|
|
436
|
+
}
|
|
390
437
|
|
|
391
|
-
|
|
392
|
-
|
|
438
|
+
function scheduleStickyRefresh(callback) {
|
|
439
|
+
if (callback) {
|
|
440
|
+
stickyRefreshCallbacks.push(callback);
|
|
441
|
+
}
|
|
393
442
|
|
|
394
|
-
|
|
395
|
-
stickyRefreshTimer = null;
|
|
396
|
-
stickyRefreshFrame = window.requestAnimationFrame(dispatchStickyRefresh);
|
|
397
|
-
}, delay);
|
|
398
|
-
}
|
|
443
|
+
if (stickyRefreshFrame) return;
|
|
399
444
|
|
|
400
|
-
|
|
401
|
-
return header.classList.contains('sticky') ||
|
|
402
|
-
header.classList.contains('sticky-left') ||
|
|
403
|
-
header.classList.contains('actions-column');
|
|
445
|
+
stickyRefreshFrame = window.requestAnimationFrame(dispatchStickyRefresh);
|
|
404
446
|
}
|
|
405
447
|
|
|
406
448
|
function sourceTableForHandle(handle) {
|
|
@@ -419,34 +461,191 @@
|
|
|
419
461
|
}) || matchingSourceTable(table) || null;
|
|
420
462
|
}
|
|
421
463
|
|
|
422
|
-
function
|
|
464
|
+
function previewBoundsForTable(table, header) {
|
|
465
|
+
var headerRect = header.getBoundingClientRect();
|
|
466
|
+
var tableRect = table.getBoundingClientRect();
|
|
467
|
+
var scrollContainer = table.closest('.sticky-table-scroll, .scroll-table, .home-table__wrapper');
|
|
468
|
+
var scrollRect = scrollContainer ? scrollContainer.getBoundingClientRect() : tableRect;
|
|
469
|
+
var headerTable = header.closest(TABLE_SELECTOR);
|
|
470
|
+
var fromFixedHeader = headerTable && headerTable.classList.contains(FIXED_HEADER_TABLE_CLASS);
|
|
471
|
+
var viewportBottom = viewportHeight();
|
|
472
|
+
var top = fromFixedHeader ? headerRect.top : Math.max(tableRect.top, scrollRect.top);
|
|
473
|
+
var bottom = Math.min(viewportBottom, Math.max(tableRect.bottom, scrollRect.bottom, headerRect.bottom));
|
|
474
|
+
|
|
475
|
+
top = Math.max(0, Math.min(top, viewportBottom));
|
|
476
|
+
if (bottom <= top) {
|
|
477
|
+
bottom = Math.min(viewportBottom, top + Math.max(headerRect.height, 32));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
left: headerRect.left,
|
|
482
|
+
top: top,
|
|
483
|
+
height: Math.max(32, bottom - top)
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function updateDragPreview(preview, width) {
|
|
488
|
+
if (!preview) return;
|
|
489
|
+
|
|
490
|
+
preview.element.style.width = cssPixelValue(width);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function createDragPreview(table, header, width) {
|
|
494
|
+
var bounds = previewBoundsForTable(table, header);
|
|
495
|
+
var element = document.createElement('div');
|
|
496
|
+
|
|
497
|
+
element.className = PREVIEW_CLASS;
|
|
498
|
+
element.setAttribute('aria-hidden', 'true');
|
|
499
|
+
element.style.left = cssPixelValue(bounds.left);
|
|
500
|
+
element.style.top = cssPixelValue(bounds.top);
|
|
501
|
+
element.style.height = cssPixelValue(bounds.height);
|
|
502
|
+
|
|
503
|
+
document.body.appendChild(element);
|
|
504
|
+
|
|
505
|
+
var preview = {
|
|
506
|
+
element: element
|
|
507
|
+
};
|
|
508
|
+
updateDragPreview(preview, width);
|
|
509
|
+
|
|
510
|
+
return preview;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function removePreview(preview) {
|
|
514
|
+
if (!preview) return;
|
|
515
|
+
|
|
516
|
+
preview.element.remove();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function removeDragPreview() {
|
|
520
|
+
if (!dragState || !dragState.preview) return;
|
|
521
|
+
|
|
522
|
+
removePreview(dragState.preview);
|
|
523
|
+
dragState.preview = null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function flushDragPreview() {
|
|
423
527
|
if (!dragState) return;
|
|
424
528
|
|
|
425
|
-
if (
|
|
426
|
-
window.cancelAnimationFrame(
|
|
427
|
-
|
|
529
|
+
if (dragPreviewFrame) {
|
|
530
|
+
window.cancelAnimationFrame(dragPreviewFrame);
|
|
531
|
+
dragPreviewFrame = null;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
updateDragPreview(dragState.preview, dragState.currentWidth || dragState.startWidth);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function releaseDragPointerCapture() {
|
|
538
|
+
if (!dragState || !dragState.handle || dragState.pointerId === null || typeof dragState.pointerId === 'undefined') return;
|
|
539
|
+
if (!dragState.handle.releasePointerCapture) return;
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
dragState.handle.releasePointerCapture(dragState.pointerId);
|
|
543
|
+
} catch (_error) {
|
|
544
|
+
// The pointer may already be released by the browser after cancellation.
|
|
428
545
|
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function captureDragPointer(handle, event) {
|
|
549
|
+
if (!handle.setPointerCapture || event.pointerId === null || typeof event.pointerId === 'undefined') return;
|
|
429
550
|
|
|
430
|
-
|
|
551
|
+
try {
|
|
552
|
+
handle.setPointerCapture(event.pointerId);
|
|
553
|
+
} catch (_error) {
|
|
554
|
+
// Pointer capture is an enhancement for touch/pen dragging; document listeners remain as fallback.
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function eventMatchesDragPointer(event) {
|
|
559
|
+
return !event ||
|
|
560
|
+
dragState.pointerId === null ||
|
|
561
|
+
typeof dragState.pointerId === 'undefined' ||
|
|
562
|
+
event.pointerId === dragState.pointerId;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function pointerCanStartDrag(event) {
|
|
566
|
+
if (event.isPrimary === false) return false;
|
|
567
|
+
|
|
568
|
+
return event.pointerType !== 'mouse' || event.button === 0;
|
|
431
569
|
}
|
|
432
570
|
|
|
433
571
|
function scheduleDragWidth(width) {
|
|
434
572
|
dragState.currentWidth = width;
|
|
435
573
|
dragState.moved = dragState.moved || Math.abs(width - dragState.startWidth) > 2;
|
|
436
574
|
|
|
437
|
-
if (
|
|
575
|
+
if (dragPreviewFrame) return;
|
|
438
576
|
|
|
439
|
-
|
|
440
|
-
|
|
577
|
+
dragPreviewFrame = window.requestAnimationFrame(function() {
|
|
578
|
+
dragPreviewFrame = null;
|
|
441
579
|
if (!dragState) return;
|
|
442
580
|
|
|
443
|
-
|
|
444
|
-
|
|
581
|
+
updateDragPreview(dragState.preview, dragState.currentWidth);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function refreshStickyHeaderColumn(pendingWidth, callback) {
|
|
586
|
+
var api = window.YummyGuideAdministrateStickyTableHeaders;
|
|
587
|
+
|
|
588
|
+
if (api && typeof api.refreshColumnWidth === 'function') {
|
|
589
|
+
var refreshed = api.refreshColumnWidth({
|
|
590
|
+
sourceTable: pendingWidth.sourceTable,
|
|
591
|
+
columnId: pendingWidth.columnId,
|
|
592
|
+
width: pendingWidth.width
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
if (refreshed) {
|
|
596
|
+
window.requestAnimationFrame(callback);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
scheduleStickyRefresh(callback);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function refreshStickyLeftColumns(sourceTable) {
|
|
605
|
+
var api = window.YummyGuideAdministrateStickyLeftColumns;
|
|
606
|
+
|
|
607
|
+
if (api && typeof api.refreshTable === 'function') {
|
|
608
|
+
api.refreshTable(sourceTable);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function stopApplyingWidth(preview) {
|
|
613
|
+
removePreview(preview);
|
|
614
|
+
applyingWidth = false;
|
|
615
|
+
document.body.classList.remove(APPLYING_BODY_CLASS);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function applyPendingWidth(pendingWidth) {
|
|
619
|
+
try {
|
|
620
|
+
var widths = safeReadWidths(pendingWidth.storageKey);
|
|
621
|
+
applyColumnWidth(pendingWidth.columnId, pendingWidth.width, pendingWidth.storageKey);
|
|
622
|
+
widths[pendingWidth.columnId] = preciseNumber(pendingWidth.width);
|
|
623
|
+
safeWriteWidths(pendingWidth.storageKey, widths);
|
|
624
|
+
refreshStickyLeftColumns(pendingWidth.sourceTable);
|
|
625
|
+
refreshStickyHeaderColumn(pendingWidth, function() {
|
|
626
|
+
stopApplyingWidth(pendingWidth.preview);
|
|
627
|
+
});
|
|
628
|
+
} catch (error) {
|
|
629
|
+
stopApplyingWidth(pendingWidth.preview);
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function schedulePendingWidthApply(pendingWidth) {
|
|
635
|
+
applyingWidth = true;
|
|
636
|
+
document.body.classList.add(APPLYING_BODY_CLASS);
|
|
637
|
+
|
|
638
|
+
widthApplyFrame = window.requestAnimationFrame(function() {
|
|
639
|
+
widthApplyFrame = window.requestAnimationFrame(function() {
|
|
640
|
+
widthApplyFrame = null;
|
|
641
|
+
applyPendingWidth(pendingWidth);
|
|
642
|
+
});
|
|
445
643
|
});
|
|
446
644
|
}
|
|
447
645
|
|
|
448
646
|
function startDrag(event) {
|
|
449
|
-
if (event
|
|
647
|
+
if (!pointerCanStartDrag(event)) return;
|
|
648
|
+
if (applyingWidth || widthApplyFrame) return;
|
|
450
649
|
|
|
451
650
|
var handle = event.currentTarget;
|
|
452
651
|
var header = handle.closest('th');
|
|
@@ -459,8 +658,11 @@
|
|
|
459
658
|
if (!sourceTable) return;
|
|
460
659
|
|
|
461
660
|
var sourceHeader = columnHeader(sourceTable, columnId) || header;
|
|
462
|
-
var
|
|
661
|
+
var sourceHeaderWidth = measuredWidth(sourceHeader);
|
|
662
|
+
var handleHeaderWidth = measuredWidth(header);
|
|
663
|
+
var startWidth = sourceHeaderWidth || handleHeaderWidth;
|
|
463
664
|
if (!startWidth) return;
|
|
665
|
+
var previewHeader = sourceHeaderWidth ? sourceHeader : header;
|
|
464
666
|
|
|
465
667
|
event.preventDefault();
|
|
466
668
|
event.stopPropagation();
|
|
@@ -471,10 +673,14 @@
|
|
|
471
673
|
startX: event.clientX,
|
|
472
674
|
startWidth: startWidth,
|
|
473
675
|
currentWidth: startWidth,
|
|
676
|
+
pointerId: event.pointerId,
|
|
677
|
+
handle: handle,
|
|
678
|
+
sourceTable: sourceTable,
|
|
474
679
|
moved: false,
|
|
475
|
-
|
|
680
|
+
preview: createDragPreview(sourceTable, previewHeader, startWidth)
|
|
476
681
|
};
|
|
477
682
|
|
|
683
|
+
captureDragPointer(handle, event);
|
|
478
684
|
document.body.classList.add(DRAGGING_BODY_CLASS);
|
|
479
685
|
document.addEventListener('pointermove', handleDragMove);
|
|
480
686
|
document.addEventListener('pointerup', finishDrag);
|
|
@@ -483,6 +689,7 @@
|
|
|
483
689
|
|
|
484
690
|
function handleDragMove(event) {
|
|
485
691
|
if (!dragState) return;
|
|
692
|
+
if (!eventMatchesDragPointer(event)) return;
|
|
486
693
|
|
|
487
694
|
event.preventDefault();
|
|
488
695
|
|
|
@@ -491,24 +698,29 @@
|
|
|
491
698
|
|
|
492
699
|
function finishDrag(event) {
|
|
493
700
|
if (!dragState) return;
|
|
701
|
+
if (!eventMatchesDragPointer(event)) return;
|
|
494
702
|
|
|
495
703
|
if (event) {
|
|
496
704
|
event.preventDefault();
|
|
497
705
|
}
|
|
498
706
|
|
|
499
|
-
|
|
707
|
+
flushDragPreview();
|
|
500
708
|
|
|
501
|
-
var
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
709
|
+
var pendingWidth = {
|
|
710
|
+
columnId: dragState.columnId,
|
|
711
|
+
storageKey: dragState.storageKey,
|
|
712
|
+
sourceTable: dragState.sourceTable,
|
|
713
|
+
width: Math.max(MIN_WIDTH, dragState.currentWidth || dragState.startWidth),
|
|
714
|
+
preview: dragState.preview
|
|
715
|
+
};
|
|
505
716
|
|
|
717
|
+
releaseDragPointerCapture();
|
|
506
718
|
dragState = null;
|
|
507
719
|
document.body.classList.remove(DRAGGING_BODY_CLASS);
|
|
508
720
|
document.removeEventListener('pointermove', handleDragMove);
|
|
509
721
|
document.removeEventListener('pointerup', finishDrag);
|
|
510
722
|
document.removeEventListener('pointercancel', finishDrag);
|
|
511
|
-
|
|
723
|
+
schedulePendingWidthApply(pendingWidth);
|
|
512
724
|
}
|
|
513
725
|
|
|
514
726
|
function resetColumn(event) {
|
|
@@ -528,7 +740,7 @@
|
|
|
528
740
|
delete widths[columnId];
|
|
529
741
|
safeWriteWidths(key, widths);
|
|
530
742
|
clearColumnWidth(columnId, key);
|
|
531
|
-
scheduleStickyRefresh(
|
|
743
|
+
scheduleStickyRefresh();
|
|
532
744
|
}
|
|
533
745
|
|
|
534
746
|
function stopHandleClick(event) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var TABLE_SELECTOR = "table[data-fixed-columns-count]";
|
|
3
3
|
var MOBILE_MEDIA_QUERY = "(max-width: 767px)";
|
|
4
4
|
var resizeObservers = new WeakMap();
|
|
5
|
+
var suppressedResizeTables = new WeakMap();
|
|
5
6
|
|
|
6
7
|
function directCells(row) {
|
|
7
8
|
return Array.from(row.children).filter(function(cell) {
|
|
@@ -100,11 +101,41 @@
|
|
|
100
101
|
});
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
function suppressResizeApply(table) {
|
|
105
|
+
if (!table) return;
|
|
106
|
+
|
|
107
|
+
var token = (suppressedResizeTables.get(table) || 0) + 1;
|
|
108
|
+
suppressedResizeTables.set(table, token);
|
|
109
|
+
|
|
110
|
+
window.setTimeout(function() {
|
|
111
|
+
if (suppressedResizeTables.get(table) === token) {
|
|
112
|
+
suppressedResizeTables.delete(table);
|
|
113
|
+
}
|
|
114
|
+
}, 250);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function resizeApplySuppressed(table) {
|
|
118
|
+
return suppressedResizeTables.has(table);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function refreshTable(table) {
|
|
122
|
+
if (!table) return false;
|
|
123
|
+
|
|
124
|
+
suppressResizeApply(table);
|
|
125
|
+
applyStickyColumns(table);
|
|
126
|
+
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
103
130
|
function observeStickyColumns(table) {
|
|
104
131
|
if (!window.ResizeObserver || resizeObservers.has(table)) return;
|
|
105
132
|
|
|
106
133
|
var observer = new ResizeObserver(function() {
|
|
134
|
+
if (resizeApplySuppressed(table)) return;
|
|
135
|
+
|
|
107
136
|
window.requestAnimationFrame(function() {
|
|
137
|
+
if (resizeApplySuppressed(table)) return;
|
|
138
|
+
|
|
108
139
|
applyStickyColumns(table);
|
|
109
140
|
});
|
|
110
141
|
});
|
|
@@ -149,6 +180,10 @@
|
|
|
149
180
|
document.addEventListener("turbo:load", initializeFromDocument);
|
|
150
181
|
window.addEventListener("resize", initializeFromDocument);
|
|
151
182
|
|
|
183
|
+
window.YummyGuideAdministrateStickyLeftColumns = {
|
|
184
|
+
refreshTable: refreshTable
|
|
185
|
+
};
|
|
186
|
+
|
|
152
187
|
if (window.MutationObserver) {
|
|
153
188
|
var mutationObserver = new MutationObserver(function(mutations) {
|
|
154
189
|
mutations.forEach(function(mutation) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
var observedScrolls = new WeakMap();
|
|
7
7
|
var observedFixedScrolls = new WeakMap();
|
|
8
8
|
var scrollSyncStates = new WeakMap();
|
|
9
|
+
var suppressedResizeScrolls = new WeakMap();
|
|
9
10
|
|
|
10
11
|
function directCells(row) {
|
|
11
12
|
return Array.from(row.children).filter(function(cell) {
|
|
@@ -36,6 +37,11 @@
|
|
|
36
37
|
return preciseNumber(Math.max(table.scrollWidth || 0, measuredWidth(table), widthsSum));
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
function parsedPixelValue(value) {
|
|
41
|
+
var parsedValue = parseFloat(value || '0');
|
|
42
|
+
return Number.isNaN(parsedValue) ? 0 : preciseNumber(parsedValue);
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
function fixedColumnsCount(table) {
|
|
40
46
|
var rawCount = table.dataset.fixedColumnsCount || '0';
|
|
41
47
|
|
|
@@ -339,6 +345,19 @@
|
|
|
339
345
|
});
|
|
340
346
|
}
|
|
341
347
|
|
|
348
|
+
function applyColGroupColumnWidth(table, index, widthValue) {
|
|
349
|
+
if (!table) return;
|
|
350
|
+
|
|
351
|
+
var colgroup = table.querySelector('colgroup[data-fixed-header-colgroup]');
|
|
352
|
+
if (!colgroup) return;
|
|
353
|
+
|
|
354
|
+
while (colgroup.children.length <= index) {
|
|
355
|
+
colgroup.appendChild(document.createElement('col'));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
colgroup.children[index].style.width = widthValue;
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
function applyFixedHeaderCellWidths(fixedTable, widths) {
|
|
343
362
|
var headerRow = fixedTable && fixedTable.querySelector('thead tr');
|
|
344
363
|
if (!headerRow) return;
|
|
@@ -356,6 +375,56 @@
|
|
|
356
375
|
});
|
|
357
376
|
}
|
|
358
377
|
|
|
378
|
+
function applyFixedHeaderCellWidth(fixedTable, index, width) {
|
|
379
|
+
var headerRow = fixedTable && fixedTable.querySelector('thead tr');
|
|
380
|
+
if (!headerRow) return;
|
|
381
|
+
|
|
382
|
+
var cell = directCells(headerRow)[index];
|
|
383
|
+
if (!cell) return;
|
|
384
|
+
|
|
385
|
+
var widthValue = cssPixelValue(width);
|
|
386
|
+
cell.style.width = widthValue;
|
|
387
|
+
cell.style.minWidth = widthValue;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function columnIdentifier(cell) {
|
|
391
|
+
if (!cell) return '';
|
|
392
|
+
|
|
393
|
+
return cell.dataset.adminColumnResizerColumnId || cell.dataset.columnId || '';
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function sourceColumnIndex(sourceTable, columnId) {
|
|
397
|
+
var sourceRow = sourceTable && sourceTable.querySelector('thead tr');
|
|
398
|
+
if (!sourceRow) return -1;
|
|
399
|
+
|
|
400
|
+
return directCells(sourceRow).findIndex(function(cell) {
|
|
401
|
+
return columnIdentifier(cell) === columnId;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function columnWidthsFromCurrentHeader(sourceTable, fixedTable, columnCount) {
|
|
406
|
+
var sourceRow = sourceTable && sourceTable.querySelector('thead tr');
|
|
407
|
+
var fixedRow = fixedTable && fixedTable.querySelector('thead tr');
|
|
408
|
+
var sourceCells = sourceRow ? directCells(sourceRow) : [];
|
|
409
|
+
var fixedCells = fixedRow ? directCells(fixedRow) : [];
|
|
410
|
+
var sourceColgroup = sourceTable && sourceTable.querySelector('colgroup[data-fixed-header-colgroup]');
|
|
411
|
+
var fixedColgroup = fixedTable && fixedTable.querySelector('colgroup[data-fixed-header-colgroup]');
|
|
412
|
+
var widths = [];
|
|
413
|
+
|
|
414
|
+
for (var index = 0; index < columnCount; index += 1) {
|
|
415
|
+
var fixedColWidth = fixedColgroup && fixedColgroup.children[index] && parsedPixelValue(fixedColgroup.children[index].style.width);
|
|
416
|
+
var sourceColWidth = sourceColgroup && sourceColgroup.children[index] && parsedPixelValue(sourceColgroup.children[index].style.width);
|
|
417
|
+
|
|
418
|
+
widths[index] = fixedColWidth ||
|
|
419
|
+
sourceColWidth ||
|
|
420
|
+
measuredWidth(fixedCells[index]) ||
|
|
421
|
+
measuredWidth(sourceCells[index]) ||
|
|
422
|
+
0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return widths;
|
|
426
|
+
}
|
|
427
|
+
|
|
359
428
|
function mergeMeasuredCellWidths(widths, cells) {
|
|
360
429
|
cells.forEach(function(cell, index) {
|
|
361
430
|
widths[index] = Math.max(widths[index] || 0, measuredWidth(cell));
|
|
@@ -467,6 +536,24 @@
|
|
|
467
536
|
});
|
|
468
537
|
}
|
|
469
538
|
|
|
539
|
+
function applySourceColumnMinWidth(sourceTable, columnIndex, columnCount, width) {
|
|
540
|
+
if (!sourceTable) return;
|
|
541
|
+
|
|
542
|
+
var widthValue = cssPixelValue(width);
|
|
543
|
+
Array.from(sourceTable.querySelectorAll('thead tr, tbody tr, tfoot tr')).forEach(function(row) {
|
|
544
|
+
var cells = directCells(row);
|
|
545
|
+
if (cells.length !== columnCount || cells.some(function(cell) { return cell.colSpan > 1; })) return;
|
|
546
|
+
|
|
547
|
+
var cell = cells[columnIndex];
|
|
548
|
+
if (!cell) return;
|
|
549
|
+
|
|
550
|
+
cell.style.setProperty('--fixed-header-column-min-width', widthValue);
|
|
551
|
+
applyManagedCellWidth(cell, 'width', widthValue);
|
|
552
|
+
applyManagedCellWidth(cell, 'min-width', widthValue);
|
|
553
|
+
cell.setAttribute('data-fixed-header-column-min-width', 'true');
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
470
557
|
function clearFixedHeaderState(slot, scroll, sourceTable) {
|
|
471
558
|
if (slot) {
|
|
472
559
|
slot.hidden = true;
|
|
@@ -534,6 +621,63 @@
|
|
|
534
621
|
});
|
|
535
622
|
}
|
|
536
623
|
|
|
624
|
+
function suppressResizeBuild(scroll) {
|
|
625
|
+
if (!scroll) return;
|
|
626
|
+
|
|
627
|
+
var token = (suppressedResizeScrolls.get(scroll) || 0) + 1;
|
|
628
|
+
suppressedResizeScrolls.set(scroll, token);
|
|
629
|
+
|
|
630
|
+
window.setTimeout(function() {
|
|
631
|
+
if (suppressedResizeScrolls.get(scroll) === token) {
|
|
632
|
+
suppressedResizeScrolls.delete(scroll);
|
|
633
|
+
}
|
|
634
|
+
}, 250);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function resizeBuildSuppressed(scroll) {
|
|
638
|
+
return suppressedResizeScrolls.has(scroll);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function refreshColumnWidth(options) {
|
|
642
|
+
var settings = options || {};
|
|
643
|
+
var sourceTable = settings.sourceTable;
|
|
644
|
+
var columnId = settings.columnId;
|
|
645
|
+
var width = parseFloat(settings.width);
|
|
646
|
+
|
|
647
|
+
if (!sourceTable || !columnId || Number.isNaN(width)) return false;
|
|
648
|
+
|
|
649
|
+
var scroll = sourceTable.closest(WRAPPER_SELECTOR);
|
|
650
|
+
if (!scroll) return false;
|
|
651
|
+
|
|
652
|
+
var slot = ensureFixedHeaderSlot(scroll);
|
|
653
|
+
var fixedScroll = slot && slot.querySelector('.table-fixed-header__scroll');
|
|
654
|
+
var fixedTable = fixedScroll && fixedScroll.querySelector('.table-fixed-header__table');
|
|
655
|
+
if (!slot || !fixedScroll || !fixedTable) return false;
|
|
656
|
+
|
|
657
|
+
var sourceRow = sourceTable.querySelector('thead tr');
|
|
658
|
+
var columnCount = sourceRow ? directCells(sourceRow).length : 0;
|
|
659
|
+
var columnIndex = sourceColumnIndex(sourceTable, columnId);
|
|
660
|
+
if (columnIndex < 0 || columnCount === 0) return false;
|
|
661
|
+
|
|
662
|
+
suppressResizeBuild(scroll);
|
|
663
|
+
|
|
664
|
+
var widthValue = cssPixelValue(width);
|
|
665
|
+
var widths = columnWidthsFromCurrentHeader(sourceTable, fixedTable, columnCount);
|
|
666
|
+
widths[columnIndex] = preciseNumber(width);
|
|
667
|
+
|
|
668
|
+
applySourceColumnMinWidth(sourceTable, columnIndex, columnCount, width);
|
|
669
|
+
applyColGroupColumnWidth(sourceTable, columnIndex, widthValue);
|
|
670
|
+
applyColGroupColumnWidth(fixedTable, columnIndex, widthValue);
|
|
671
|
+
applyFixedHeaderCellWidth(fixedTable, columnIndex, width);
|
|
672
|
+
applyFixedHeaderStickyColumns(sourceTable, fixedTable, widths);
|
|
673
|
+
|
|
674
|
+
fixedTable.style.width = cssPixelValue(measuredTableWidth(sourceTable, widths));
|
|
675
|
+
syncStickyPageHeader(scroll, slot);
|
|
676
|
+
syncFixedHeaderScroll(scroll, fixedScroll);
|
|
677
|
+
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
|
|
537
681
|
function buildFixedTableHeader(slot, scroll, sourceTable) {
|
|
538
682
|
if (!slot || !scroll || !sourceTable) return;
|
|
539
683
|
|
|
@@ -612,7 +756,11 @@
|
|
|
612
756
|
var observer = resizeObservers.get(scroll);
|
|
613
757
|
if (!observer) {
|
|
614
758
|
observer = new ResizeObserver(function() {
|
|
759
|
+
if (resizeBuildSuppressed(scroll)) return;
|
|
760
|
+
|
|
615
761
|
window.requestAnimationFrame(function() {
|
|
762
|
+
if (resizeBuildSuppressed(scroll)) return;
|
|
763
|
+
|
|
616
764
|
initializeFixedHeaderForScroll(scroll);
|
|
617
765
|
});
|
|
618
766
|
});
|
|
@@ -721,6 +869,10 @@
|
|
|
721
869
|
document.addEventListener('turbo:load', initializeFromDocument);
|
|
722
870
|
window.addEventListener('resize', initializeFromDocument);
|
|
723
871
|
|
|
872
|
+
window.YummyGuideAdministrateStickyTableHeaders = {
|
|
873
|
+
refreshColumnWidth: refreshColumnWidth
|
|
874
|
+
};
|
|
875
|
+
|
|
724
876
|
if (window.MutationObserver) {
|
|
725
877
|
var mutationObserver = new MutationObserver(function(mutations) {
|
|
726
878
|
mutations.forEach(function(mutation) {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
table.admin-column-resizer__table:not(.table-fixed-header__table) {
|
|
2
|
+
table-layout: auto !important;
|
|
3
|
+
width: max-content !important;
|
|
4
|
+
min-width: 100% !important;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
.admin-column-resizer__header {
|
|
2
8
|
position: relative;
|
|
3
9
|
}
|
|
@@ -7,7 +13,7 @@
|
|
|
7
13
|
top: 0;
|
|
8
14
|
right: -5px;
|
|
9
15
|
bottom: 0;
|
|
10
|
-
z-index:
|
|
16
|
+
z-index: 1;
|
|
11
17
|
width: 12px;
|
|
12
18
|
cursor: col-resize;
|
|
13
19
|
touch-action: none;
|
|
@@ -42,6 +48,25 @@
|
|
|
42
48
|
user-select: none !important;
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
.admin-column-resizer--applying,
|
|
52
|
+
.admin-column-resizer--applying * {
|
|
53
|
+
cursor: wait !important;
|
|
54
|
+
user-select: none !important;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.admin-column-resizer__preview {
|
|
58
|
+
position: fixed;
|
|
59
|
+
z-index: 10000;
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
background: rgba(37, 99, 235, 0.12);
|
|
63
|
+
border-right: 2px solid rgba(37, 99, 235, 0.85);
|
|
64
|
+
border-left: 1px solid rgba(37, 99, 235, 0.35);
|
|
65
|
+
box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.12);
|
|
66
|
+
contain: layout paint style;
|
|
67
|
+
will-change: width;
|
|
68
|
+
}
|
|
69
|
+
|
|
45
70
|
[data-fixed-table-header] .admin-column-resizer__handle {
|
|
46
71
|
pointer-events: auto;
|
|
47
72
|
}
|
|
@@ -52,13 +77,17 @@
|
|
|
52
77
|
|
|
53
78
|
@media (pointer: coarse), screen and (max-width: 767px) {
|
|
54
79
|
.admin-column-resizer__handle {
|
|
55
|
-
right: -
|
|
56
|
-
width:
|
|
80
|
+
right: -14px;
|
|
81
|
+
width: 36px;
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
.admin-column-resizer__handle::after {
|
|
60
|
-
right:
|
|
85
|
+
right: 17px;
|
|
61
86
|
background: rgba(255, 255, 255, 0.75);
|
|
62
87
|
opacity: 1;
|
|
63
88
|
}
|
|
89
|
+
|
|
90
|
+
.admin-column-resizer__preview {
|
|
91
|
+
border-right-width: 3px;
|
|
92
|
+
}
|
|
64
93
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "column resizer assets" do
|
|
4
|
+
let(:javascript_source) do
|
|
5
|
+
File.read(File.expand_path("../../../app/assets/javascripts/yummy_guide_administrate/column_resizer.js", __dir__))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:stylesheet_source) do
|
|
9
|
+
File.read(File.expand_path("../../../app/assets/stylesheets/yummy_guide_administrate/_column_resizer.scss", __dir__))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:sticky_table_headers_source) do
|
|
13
|
+
File.read(File.expand_path("../../../app/assets/javascripts/yummy_guide_administrate/sticky_table_headers.js", __dir__))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:sticky_left_columns_source) do
|
|
17
|
+
File.read(File.expand_path("../../../app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js", __dir__))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# 未調整列は内容幅で表示され、調整済み列だけが幅固定されることを静的に確認する
|
|
21
|
+
it "scopes fixed width rules to adjusted columns" do
|
|
22
|
+
expect(javascript_source).to include("data-admin-column-resizer-adjusted-columns")
|
|
23
|
+
expect(javascript_source).to include("setAdjustedColumn(table, index, true)")
|
|
24
|
+
expect(javascript_source).to include("setAdjustedColumn(table, index, false)")
|
|
25
|
+
expect(javascript_source).to include("ADJUSTED_COLUMNS_ATTRIBUTE + '~=\"' + nthChild")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# 幅調整後の内容が常に折り返されるCSSが生成されることを確認する
|
|
29
|
+
it "generates wrapping rules for adjusted columns" do
|
|
30
|
+
expect(javascript_source).to include("white-space: normal !important")
|
|
31
|
+
expect(javascript_source).to include("overflow-wrap: anywhere !important")
|
|
32
|
+
expect(javascript_source).to include("word-break: break-word !important")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# デフォルト状態の列幅が内容の最大幅を基準に決まることを確認する
|
|
36
|
+
it "uses max-content table sizing by default" do
|
|
37
|
+
expect(stylesheet_source).to include("table-layout: auto !important")
|
|
38
|
+
expect(stylesheet_source).to include("width: max-content !important")
|
|
39
|
+
expect(stylesheet_source).to include("min-width: 100% !important")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# ドラッグ中に固定ヘッダー同期を繰り返さず、調整完了後だけ同期することを静的に確認する
|
|
43
|
+
it "synchronizes fixed headers after drag completion instead of during drag" do
|
|
44
|
+
expect(javascript_source).not_to include("scheduleStickyRefresh(false)")
|
|
45
|
+
expect(javascript_source).not_to include("refreshDuringDrag")
|
|
46
|
+
expect(javascript_source).not_to include("columnNeedsDragRefresh")
|
|
47
|
+
expect(javascript_source).not_to include("STICKY_REFRESH_INTERVAL")
|
|
48
|
+
expect(javascript_source).to include("refreshStickyHeaderColumn(pendingWidth, function()")
|
|
49
|
+
expect(javascript_source).to include("scheduleStickyRefresh(callback)")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# ドラッグ中は実テーブルを再レイアウトせず、プレビューだけを更新することを静的に確認する
|
|
53
|
+
it "updates only the lightweight preview while dragging" do
|
|
54
|
+
expect(javascript_source).to include("createDragPreview(sourceTable, previewHeader, startWidth)")
|
|
55
|
+
expect(javascript_source).to include("updateDragPreview(dragState.preview, dragState.currentWidth)")
|
|
56
|
+
expect(javascript_source).to include("schedulePendingWidthApply(pendingWidth)")
|
|
57
|
+
expect(javascript_source).to include("applyColumnWidth(pendingWidth.columnId, pendingWidth.width, pendingWidth.storageKey)")
|
|
58
|
+
expect(javascript_source).to include("sourceTable: dragState.sourceTable")
|
|
59
|
+
expect(javascript_source).not_to include("applyColumnWidth(dragState.columnId, dragState.currentWidth")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ドラッグ中の調整後幅を半透明カラムで表示するCSSがあることを確認する
|
|
63
|
+
it "defines a translucent column preview" do
|
|
64
|
+
expect(stylesheet_source).to include(".admin-column-resizer__preview")
|
|
65
|
+
expect(stylesheet_source).to include("pointer-events: none")
|
|
66
|
+
expect(stylesheet_source).to include("will-change: width")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# 幅調整中のpxラベルが表示されないことを静的に確認する
|
|
70
|
+
it "does not render a pixel width label in the preview" do
|
|
71
|
+
expect(javascript_source).not_to include("PREVIEW_LABEL_CLASS")
|
|
72
|
+
expect(javascript_source).not_to include("admin-column-resizer__preview-label")
|
|
73
|
+
expect(javascript_source).not_to include("Math.round(width) + 'px'")
|
|
74
|
+
expect(stylesheet_source).not_to include(".admin-column-resizer__preview-label")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# 固定列ヘッダーより幅調整ハンドルを背面にすることを静的に確認する
|
|
78
|
+
it "keeps resize handles behind sticky column headers" do
|
|
79
|
+
expect(stylesheet_source).to include("z-index: 1")
|
|
80
|
+
expect(stylesheet_source).not_to include("z-index: 20")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# 幅の適用中だけ待機カーソルを表示することを静的に確認する
|
|
84
|
+
it "shows a wait cursor while applying the final width" do
|
|
85
|
+
expect(javascript_source).to include("APPLYING_BODY_CLASS = 'admin-column-resizer--applying'")
|
|
86
|
+
expect(javascript_source).to include("document.body.classList.add(APPLYING_BODY_CLASS)")
|
|
87
|
+
expect(javascript_source).to include("document.body.classList.remove(APPLYING_BODY_CLASS)")
|
|
88
|
+
expect(stylesheet_source).to include(".admin-column-resizer--applying")
|
|
89
|
+
expect(stylesheet_source).to include("cursor: wait !important")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# モバイルのtouch/pen操作でハンドル外へ移動しても調整を継続できることを静的に確認する
|
|
93
|
+
it "supports touch and pen pointer dragging on mobile" do
|
|
94
|
+
expect(javascript_source).to include("event.pointerType !== 'mouse' || event.button === 0")
|
|
95
|
+
expect(javascript_source).to include("event.isPrimary === false")
|
|
96
|
+
expect(javascript_source).to include("handle.setPointerCapture(event.pointerId)")
|
|
97
|
+
expect(javascript_source).to include("dragState.handle.releasePointerCapture(dragState.pointerId)")
|
|
98
|
+
expect(stylesheet_source).to include("right: -14px")
|
|
99
|
+
expect(stylesheet_source).to include("width: 36px")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# 幅適用後の固定ヘッダー追従が全体rebuildではなく対象カラム同期を優先することを静的に確認する
|
|
103
|
+
it "uses targeted fixed header synchronization after applying a width" do
|
|
104
|
+
expect(javascript_source).to include("window.YummyGuideAdministrateStickyTableHeaders")
|
|
105
|
+
expect(javascript_source).to include("api.refreshColumnWidth({")
|
|
106
|
+
expect(sticky_table_headers_source).to include("window.YummyGuideAdministrateStickyTableHeaders = {")
|
|
107
|
+
expect(sticky_table_headers_source).to include("refreshColumnWidth: refreshColumnWidth")
|
|
108
|
+
expect(sticky_table_headers_source).to include("function refreshColumnWidth(options)")
|
|
109
|
+
expect(sticky_table_headers_source).to include("applySourceColumnMinWidth(sourceTable, columnIndex, columnCount, width)")
|
|
110
|
+
expect(sticky_table_headers_source).to include("applyFixedHeaderCellWidth(fixedTable, columnIndex, width)")
|
|
111
|
+
expect(sticky_table_headers_source).to include("suppressResizeBuild(scroll)")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# 幅適用後の固定左列追従もResizeObserverの重複再計算に頼らないことを静的に確認する
|
|
115
|
+
it "refreshes sticky left columns directly after applying a width" do
|
|
116
|
+
expect(javascript_source).to include("window.YummyGuideAdministrateStickyLeftColumns")
|
|
117
|
+
expect(javascript_source).to include("api.refreshTable(sourceTable)")
|
|
118
|
+
expect(javascript_source).to include("refreshStickyLeftColumns(pendingWidth.sourceTable)")
|
|
119
|
+
expect(sticky_left_columns_source).to include("window.YummyGuideAdministrateStickyLeftColumns = {")
|
|
120
|
+
expect(sticky_left_columns_source).to include("refreshTable: refreshTable")
|
|
121
|
+
expect(sticky_left_columns_source).to include("function refreshTable(table)")
|
|
122
|
+
expect(sticky_left_columns_source).to include("suppressResizeApply(table)")
|
|
123
|
+
end
|
|
124
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yummy-guide-generic-administrate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- akatsuki-kk
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: administrate
|
|
@@ -142,6 +142,7 @@ files:
|
|
|
142
142
|
- spec/yummy/guide/generic/administrate_spec.rb
|
|
143
143
|
- spec/yummy_guide/administrate/application_dashboard_spec.rb
|
|
144
144
|
- spec/yummy_guide/administrate/collection_helper_spec.rb
|
|
145
|
+
- spec/yummy_guide/administrate/column_resizer_asset_spec.rb
|
|
145
146
|
- spec/yummy_guide/administrate/datetime_filter_parameters_spec.rb
|
|
146
147
|
- spec/yummy_guide/administrate/datetime_input_helper_spec.rb
|
|
147
148
|
- spec/yummy_guide/administrate/fields/json_pretty_field_spec.rb
|