yummy-guide-generic-administrate 0.7.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cef007572ee8fc90b0579c0e6261263bb568d192702eb92ee418976a06404ffc
4
- data.tar.gz: be42920844cf830f365d2297e21605ad5191257d5a3f37ba2dd9f364ae16c768
3
+ metadata.gz: b23f3b7fd7d17420fdeaf7325f51f1f70b93fb86497bcdb6621b617b5086d926
4
+ data.tar.gz: 38004b14e38a03b882d6e84253fc63ad1a2e8480ab287ee27ced1d13e81239a7
5
5
  SHA512:
6
- metadata.gz: 1f9203647f13a0974af5bd4b0de54ff23c2a8991ce0eb3e065a3c0d561b1f88898c9bc470642c294bf786f5250eab2f9d5780c91b35d3f6b3cc1da9f36e2a305
7
- data.tar.gz: b5d15a4f6ca3e708542fab9b1ce320ee76e45f3774bf775394cbff9548f4aca75f945259ef5e1a4c703a5ac5a3baa604a6933a6171868e83633f203698119866
6
+ metadata.gz: ecc7d99ccc8f23f5b4d0dd9288a9d6f870f95dc9c2d19256865828fa9a3d1f10d322fd37d627d2e85d648e13ab1fb128fb2d0430035451147fa2f4b72739bbb4
7
+ data.tar.gz: 05b2097517ad48edd1e8f54230bd3f2d1ea5e591dccc733d0397e1ec89d7d3b855920066296b4119c6c4b7b46572413ca07730dae2663e584cf403d1bc0f6cc9
data/README.md CHANGED
@@ -138,7 +138,8 @@ engine の共通 partial に委譲します。
138
138
 
139
139
  gem 付属の collection partial をそのまま使う場合、table wrapper と table 本体に
140
140
  必要な `data-*` 属性はすでに入っています。そのため、JS / CSS を読み込めば固定
141
- ヘッダーは自動で有効になります。
141
+ ヘッダーとカラム幅調整は自動で有効になります。カラム幅調整を使う場合は
142
+ `yummy_guide_administrate/column_resizer.js` も読み込んでください。
142
143
 
143
144
  内部的には以下のような構造になります。
144
145
 
@@ -154,6 +155,8 @@ gem 付属の collection partial をそのまま使う場合、table wrapper と
154
155
  </div>
155
156
  ```
156
157
 
158
+ 複数画面で幅設定を共有したい場合は、render local に `column_width_storage_scope: "admin.reservations"` のような任意の scope 名を渡してください。
159
+
157
160
  #### 2. ヘッダー位置を明示したい場合
158
161
 
159
162
  固定ヘッダーの表示位置をページ上部の特定箇所に合わせたい場合は、
@@ -0,0 +1,610 @@
1
+ (function() {
2
+ var TABLE_SELECTOR = 'table[data-fixed-columns-count]';
3
+ var HANDLE_CLASS = 'admin-column-resizer__handle';
4
+ var HEADER_CLASS = 'admin-column-resizer__header';
5
+ var TABLE_CLASS = 'admin-column-resizer__table';
6
+ var DRAGGING_BODY_CLASS = 'admin-column-resizer--dragging';
7
+ var FIXED_HEADER_TABLE_CLASS = 'table-fixed-header__table';
8
+ var STORAGE_PREFIX = 'yummyGuideAdminColumnWidths:v1:';
9
+ var STYLE_ELEMENT_ID = 'admin-column-resizer-rules';
10
+ var WIDTH_VAR_PREFIX = '--admin-column-resizer-col-';
11
+ var MIN_WIDTH = 48;
12
+ var STICKY_REFRESH_INTERVAL = 120;
13
+
14
+ var dragState = null;
15
+ var dragApplyFrame = null;
16
+ var generatedRuleCount = 0;
17
+ var initializedHandles = new WeakSet();
18
+ var tableStates = new WeakMap();
19
+ var stickyRefreshFrame = null;
20
+ var stickyRefreshTimer = null;
21
+ var lastStickyRefreshAt = 0;
22
+
23
+ function storageScopeForTable(table) {
24
+ var sourceTable = matchingSourceTable(table);
25
+ var scopedTable = sourceTable || table;
26
+ var scope = scopedTable && scopedTable.getAttribute('data-column-resizer-storage-scope');
27
+
28
+ return scope || window.location.pathname;
29
+ }
30
+
31
+ function storageKeyForTable(table) {
32
+ return STORAGE_PREFIX + storageScopeForTable(table);
33
+ }
34
+
35
+ function parsedWidths(rawWidths) {
36
+ if (!rawWidths) return {};
37
+
38
+ var widths = JSON.parse(rawWidths);
39
+ return widths && typeof widths === 'object' && !Array.isArray(widths) ? widths : {};
40
+ }
41
+
42
+ function safeReadWidths(key) {
43
+ try {
44
+ return parsedWidths(window.localStorage.getItem(key));
45
+ } catch (_error) {
46
+ return {};
47
+ }
48
+ }
49
+
50
+ function safeWriteWidths(key, widths) {
51
+ try {
52
+ if (Object.keys(widths).length === 0) {
53
+ window.localStorage.removeItem(key);
54
+ } else {
55
+ window.localStorage.setItem(key, JSON.stringify(widths));
56
+ }
57
+ } catch (_error) {
58
+ // localStorage may be unavailable in private browsing or restricted contexts.
59
+ }
60
+ }
61
+
62
+ function preciseNumber(value) {
63
+ return Math.round(value * 1000) / 1000;
64
+ }
65
+
66
+ function cssPixelValue(value) {
67
+ return preciseNumber(value) + 'px';
68
+ }
69
+
70
+ function measuredWidth(element) {
71
+ if (!element) return 0;
72
+
73
+ var rectWidth = element.getBoundingClientRect().width;
74
+ return preciseNumber(rectWidth || element.offsetWidth || 0);
75
+ }
76
+
77
+ function normalizedIdentifier(value) {
78
+ return (value || '').toString().trim().toLowerCase()
79
+ .replace(/[^a-z0-9]+/g, '_')
80
+ .replace(/^_+|_+$/g, '')
81
+ .slice(0, 80) || 'column';
82
+ }
83
+
84
+ function directCells(row) {
85
+ return Array.from(row.children).filter(function(cell) {
86
+ return cell.tagName === 'TH' || cell.tagName === 'TD';
87
+ });
88
+ }
89
+
90
+ function columnHeaders(table) {
91
+ var row = table.querySelector('thead tr');
92
+ if (!row) return [];
93
+
94
+ return directCells(row).filter(function(cell) {
95
+ return cell.tagName === 'TH';
96
+ });
97
+ }
98
+
99
+ function headerLabel(header) {
100
+ return (header.getAttribute('aria-label') || header.getAttribute('title') || header.textContent || '')
101
+ .trim()
102
+ .replace(/\s+/g, ' ');
103
+ }
104
+
105
+ function headerSignature(table) {
106
+ return columnHeaders(table).map(function(header, index) {
107
+ return normalizedIdentifier(header.dataset.columnId || headerLabel(header) || ('column_' + (index + 1)));
108
+ }).join('__');
109
+ }
110
+
111
+ function matchingSourceTable(table) {
112
+ if (!table) return null;
113
+ if (!table.classList.contains(FIXED_HEADER_TABLE_CLASS)) return null;
114
+
115
+ var signature = headerSignature(table);
116
+ return sourceTables().find(function(sourceTable) {
117
+ return headerSignature(sourceTable) === signature;
118
+ }) || null;
119
+ }
120
+
121
+ function tableIdentifier(table) {
122
+ if (table.dataset.adminColumnResizerTableId) return table.dataset.adminColumnResizerTableId;
123
+
124
+ var sourceTable = matchingSourceTable(table);
125
+ if (sourceTable) {
126
+ table.dataset.adminColumnResizerTableId = tableIdentifier(sourceTable);
127
+ return table.dataset.adminColumnResizerTableId;
128
+ }
129
+
130
+ var tableLabel = table.id ||
131
+ table.getAttribute('aria-labelledby') ||
132
+ table.getAttribute('data-admin-column-resizer-table') ||
133
+ table.getAttribute('data-fixed-header-source');
134
+
135
+ if (!tableLabel) {
136
+ var index = sourceTables().indexOf(table);
137
+ tableLabel = 'table_' + (index >= 0 ? index + 1 : allTrackedTables().indexOf(table) + 1);
138
+ }
139
+
140
+ table.dataset.adminColumnResizerTableId = normalizedIdentifier(tableLabel);
141
+ return table.dataset.adminColumnResizerTableId;
142
+ }
143
+
144
+ function fallbackColumnId(table, header, index) {
145
+ return [
146
+ 'fallback',
147
+ tableIdentifier(table),
148
+ index + 1,
149
+ normalizedIdentifier(headerLabel(header))
150
+ ].join('.');
151
+ }
152
+
153
+ function headerColumnId(header) {
154
+ return header.dataset.adminColumnResizerColumnId || header.dataset.columnId || '';
155
+ }
156
+
157
+ function assignHeaderColumnId(table, header, index) {
158
+ if (header.colSpan && header.colSpan > 1) return '';
159
+
160
+ var columnId = header.dataset.columnId || fallbackColumnId(table, header, index);
161
+ header.dataset.adminColumnResizerColumnId = columnId;
162
+ return columnId;
163
+ }
164
+
165
+ function columnWidthVariable(index) {
166
+ return WIDTH_VAR_PREFIX + (index + 1);
167
+ }
168
+
169
+ function columnRule(index) {
170
+ var nthChild = index + 1;
171
+ var variableName = columnWidthVariable(index);
172
+ var selector = [
173
+ '.' + TABLE_CLASS + ' > thead > tr > :nth-child(' + nthChild + ')',
174
+ '.' + TABLE_CLASS + ' > tbody > tr > :nth-child(' + nthChild + ')',
175
+ '.' + TABLE_CLASS + ' > tfoot > tr > :nth-child(' + nthChild + ')'
176
+ ].join(', ');
177
+
178
+ return selector + ' { box-sizing: border-box !important; width: var(' + variableName + ') !important; min-width: var(' + variableName + ') !important; max-width: var(' + variableName + ') !important; }';
179
+ }
180
+
181
+ function ensureStyleElement() {
182
+ var styleElement = document.getElementById(STYLE_ELEMENT_ID);
183
+ if (styleElement) return styleElement;
184
+
185
+ styleElement = document.createElement('style');
186
+ styleElement.id = STYLE_ELEMENT_ID;
187
+ document.head.appendChild(styleElement);
188
+
189
+ return styleElement;
190
+ }
191
+
192
+ function ensureColumnRules(columnCount) {
193
+ if (columnCount <= generatedRuleCount) return;
194
+
195
+ var rules = [];
196
+ for (var index = generatedRuleCount; index < columnCount; index += 1) {
197
+ rules.push(columnRule(index));
198
+ }
199
+
200
+ ensureStyleElement().appendChild(document.createTextNode(rules.join('\n') + '\n'));
201
+ generatedRuleCount = columnCount;
202
+ }
203
+
204
+ function tablesFromRoot(root) {
205
+ var tables = [];
206
+
207
+ function appendTable(table) {
208
+ if (table && tables.indexOf(table) === -1) {
209
+ tables.push(table);
210
+ }
211
+ }
212
+
213
+ if (root.closest) {
214
+ appendTable(root.closest(TABLE_SELECTOR));
215
+ }
216
+
217
+ if (root.matches && root.matches(TABLE_SELECTOR)) {
218
+ appendTable(root);
219
+ }
220
+
221
+ if (root.querySelectorAll) {
222
+ root.querySelectorAll(TABLE_SELECTOR).forEach(appendTable);
223
+ }
224
+
225
+ return tables;
226
+ }
227
+
228
+ function shouldInitializeForAddedNode(node) {
229
+ if (!node.matches) return false;
230
+ if (node.matches(TABLE_SELECTOR) || (node.querySelector && node.querySelector(TABLE_SELECTOR))) return true;
231
+ if (!node.closest || !node.closest(TABLE_SELECTOR)) return false;
232
+
233
+ return !!node.closest('thead') || node.matches('colgroup, col');
234
+ }
235
+
236
+ function allTrackedTables() {
237
+ return Array.from(document.querySelectorAll(TABLE_SELECTOR));
238
+ }
239
+
240
+ function sourceTables() {
241
+ return allTrackedTables().filter(function(table) {
242
+ return table.getAttribute('aria-hidden') !== 'true' && !table.classList.contains(FIXED_HEADER_TABLE_CLASS);
243
+ });
244
+ }
245
+
246
+ function stateForTable(table) {
247
+ return tableStates.get(table) || configureTable(table);
248
+ }
249
+
250
+ function ensureManagedColgroup(table, columnCount) {
251
+ var fixedHeaderColgroup = table.querySelector('colgroup[data-fixed-header-colgroup]');
252
+ var colgroup = fixedHeaderColgroup || table.querySelector('colgroup[data-admin-column-resizer-colgroup]');
253
+
254
+ if (!colgroup) {
255
+ colgroup = document.createElement('colgroup');
256
+ colgroup.setAttribute('data-admin-column-resizer-colgroup', 'true');
257
+ table.insertBefore(colgroup, table.firstChild);
258
+ }
259
+
260
+ while (colgroup.children.length < columnCount) {
261
+ colgroup.appendChild(document.createElement('col'));
262
+ }
263
+
264
+ return colgroup;
265
+ }
266
+
267
+ function configureTable(table) {
268
+ var headers = columnHeaders(table);
269
+ if (headers.length === 0) return null;
270
+
271
+ var indexByColumnId = Object.create(null);
272
+
273
+ headers.forEach(function(header, index) {
274
+ var columnId = assignHeaderColumnId(table, header, index);
275
+ if (!columnId) return;
276
+
277
+ if (indexByColumnId[columnId] === undefined) {
278
+ indexByColumnId[columnId] = index;
279
+ }
280
+
281
+ ensureHandle(header);
282
+ });
283
+
284
+ table.classList.add(TABLE_CLASS);
285
+ ensureColumnRules(headers.length);
286
+ ensureManagedColgroup(table, headers.length);
287
+
288
+ var state = {
289
+ columnCount: headers.length,
290
+ indexByColumnId: indexByColumnId
291
+ };
292
+ tableStates.set(table, state);
293
+
294
+ return state;
295
+ }
296
+
297
+ function columnIndex(table, columnId) {
298
+ var state = stateForTable(table);
299
+ if (!state || state.indexByColumnId[columnId] === undefined) return -1;
300
+
301
+ return state.indexByColumnId[columnId];
302
+ }
303
+
304
+ function columnHeader(table, columnId) {
305
+ var index = columnIndex(table, columnId);
306
+ if (index < 0) return null;
307
+
308
+ return columnHeaders(table)[index] || null;
309
+ }
310
+
311
+ function applyColgroupWidth(table, columnCount, index, widthValue) {
312
+ var colgroup = ensureManagedColgroup(table, columnCount);
313
+ if (!colgroup || !colgroup.children[index]) return;
314
+
315
+ colgroup.children[index].style.width = widthValue;
316
+ }
317
+
318
+ function clearColgroupWidth(table, columnCount, index) {
319
+ var colgroup = ensureManagedColgroup(table, columnCount);
320
+ if (!colgroup || !colgroup.children[index]) return;
321
+
322
+ colgroup.children[index].style.removeProperty('width');
323
+ }
324
+
325
+ function applyTableColumnWidth(table, columnId, width) {
326
+ var state = stateForTable(table);
327
+ var index = state && state.indexByColumnId[columnId];
328
+ if (!state || index === undefined) return;
329
+
330
+ var widthValue = cssPixelValue(width);
331
+ table.style.setProperty(columnWidthVariable(index), widthValue);
332
+ applyColgroupWidth(table, state.columnCount, index, widthValue);
333
+ }
334
+
335
+ function clearTableColumnWidth(table, columnId) {
336
+ var state = stateForTable(table);
337
+ var index = state && state.indexByColumnId[columnId];
338
+ if (!state || index === undefined) return;
339
+
340
+ table.style.removeProperty(columnWidthVariable(index));
341
+ clearColgroupWidth(table, state.columnCount, index);
342
+ }
343
+
344
+ function applyColumnWidth(columnId, width, key) {
345
+ allTrackedTables().forEach(function(table) {
346
+ if (key && storageKeyForTable(table) !== key) return;
347
+
348
+ applyTableColumnWidth(table, columnId, width);
349
+ });
350
+ }
351
+
352
+ function clearColumnWidth(columnId, key) {
353
+ allTrackedTables().forEach(function(table) {
354
+ if (key && storageKeyForTable(table) !== key) return;
355
+
356
+ clearTableColumnWidth(table, columnId);
357
+ });
358
+ }
359
+
360
+ function applyStoredWidthsToTable(table, widths) {
361
+ Object.keys(widths).forEach(function(columnId) {
362
+ var width = parseFloat(widths[columnId]);
363
+ if (Number.isNaN(width) || width < MIN_WIDTH) return;
364
+
365
+ applyTableColumnWidth(table, columnId, width);
366
+ });
367
+ }
368
+
369
+ function applyStoredWidthsToTables(tables) {
370
+ if (dragState) return;
371
+
372
+ var widthsByStorageKey = Object.create(null);
373
+
374
+ tables.forEach(function(table) {
375
+ var key = storageKeyForTable(table);
376
+ widthsByStorageKey[key] = widthsByStorageKey[key] || safeReadWidths(key);
377
+
378
+ applyStoredWidthsToTable(table, widthsByStorageKey[key]);
379
+ });
380
+ }
381
+
382
+ function dispatchStickyRefresh() {
383
+ stickyRefreshFrame = null;
384
+ lastStickyRefreshAt = Date.now();
385
+ window.dispatchEvent(new Event('resize'));
386
+ }
387
+
388
+ function scheduleStickyRefresh(immediate) {
389
+ if (stickyRefreshFrame || stickyRefreshTimer) return;
390
+
391
+ var elapsed = Date.now() - lastStickyRefreshAt;
392
+ var delay = immediate ? 0 : Math.max(0, STICKY_REFRESH_INTERVAL - elapsed);
393
+
394
+ stickyRefreshTimer = window.setTimeout(function() {
395
+ stickyRefreshTimer = null;
396
+ stickyRefreshFrame = window.requestAnimationFrame(dispatchStickyRefresh);
397
+ }, delay);
398
+ }
399
+
400
+ function columnNeedsDragRefresh(header) {
401
+ return header.classList.contains('sticky') ||
402
+ header.classList.contains('sticky-left') ||
403
+ header.classList.contains('actions-column');
404
+ }
405
+
406
+ function sourceTableForHandle(handle) {
407
+ var header = handle.closest('th');
408
+ if (!header) return null;
409
+
410
+ var table = handle.closest(TABLE_SELECTOR);
411
+ var columnId = headerColumnId(header);
412
+
413
+ if (table && table.getAttribute('aria-hidden') !== 'true' && !table.classList.contains(FIXED_HEADER_TABLE_CLASS)) {
414
+ return table;
415
+ }
416
+
417
+ return sourceTables().find(function(sourceTable) {
418
+ return columnHeader(sourceTable, columnId);
419
+ }) || matchingSourceTable(table) || null;
420
+ }
421
+
422
+ function flushDragWidth() {
423
+ if (!dragState) return;
424
+
425
+ if (dragApplyFrame) {
426
+ window.cancelAnimationFrame(dragApplyFrame);
427
+ dragApplyFrame = null;
428
+ }
429
+
430
+ applyColumnWidth(dragState.columnId, dragState.currentWidth || dragState.startWidth, dragState.storageKey);
431
+ }
432
+
433
+ function scheduleDragWidth(width) {
434
+ dragState.currentWidth = width;
435
+ dragState.moved = dragState.moved || Math.abs(width - dragState.startWidth) > 2;
436
+
437
+ if (dragApplyFrame) return;
438
+
439
+ dragApplyFrame = window.requestAnimationFrame(function() {
440
+ dragApplyFrame = null;
441
+ if (!dragState) return;
442
+
443
+ applyColumnWidth(dragState.columnId, dragState.currentWidth, dragState.storageKey);
444
+ if (dragState.refreshDuringDrag) scheduleStickyRefresh(false);
445
+ });
446
+ }
447
+
448
+ function startDrag(event) {
449
+ if (event.button !== 0) return;
450
+
451
+ var handle = event.currentTarget;
452
+ var header = handle.closest('th');
453
+ if (!header) return;
454
+
455
+ var columnId = headerColumnId(header);
456
+ if (!columnId) return;
457
+
458
+ var sourceTable = sourceTableForHandle(handle);
459
+ if (!sourceTable) return;
460
+
461
+ var sourceHeader = columnHeader(sourceTable, columnId) || header;
462
+ var startWidth = measuredWidth(sourceHeader) || measuredWidth(header);
463
+ if (!startWidth) return;
464
+
465
+ event.preventDefault();
466
+ event.stopPropagation();
467
+
468
+ dragState = {
469
+ columnId: columnId,
470
+ storageKey: storageKeyForTable(sourceTable),
471
+ startX: event.clientX,
472
+ startWidth: startWidth,
473
+ currentWidth: startWidth,
474
+ moved: false,
475
+ refreshDuringDrag: columnNeedsDragRefresh(sourceHeader)
476
+ };
477
+
478
+ document.body.classList.add(DRAGGING_BODY_CLASS);
479
+ document.addEventListener('pointermove', handleDragMove);
480
+ document.addEventListener('pointerup', finishDrag);
481
+ document.addEventListener('pointercancel', finishDrag);
482
+ }
483
+
484
+ function handleDragMove(event) {
485
+ if (!dragState) return;
486
+
487
+ event.preventDefault();
488
+
489
+ scheduleDragWidth(Math.max(MIN_WIDTH, dragState.startWidth + event.clientX - dragState.startX));
490
+ }
491
+
492
+ function finishDrag(event) {
493
+ if (!dragState) return;
494
+
495
+ if (event) {
496
+ event.preventDefault();
497
+ }
498
+
499
+ flushDragWidth();
500
+
501
+ var widths = safeReadWidths(dragState.storageKey);
502
+ var width = Math.max(MIN_WIDTH, dragState.currentWidth || dragState.startWidth);
503
+ widths[dragState.columnId] = preciseNumber(width);
504
+ safeWriteWidths(dragState.storageKey, widths);
505
+
506
+ dragState = null;
507
+ document.body.classList.remove(DRAGGING_BODY_CLASS);
508
+ document.removeEventListener('pointermove', handleDragMove);
509
+ document.removeEventListener('pointerup', finishDrag);
510
+ document.removeEventListener('pointercancel', finishDrag);
511
+ scheduleStickyRefresh(true);
512
+ }
513
+
514
+ function resetColumn(event) {
515
+ var handle = event.currentTarget;
516
+ var header = handle.closest('th');
517
+ if (!header) return;
518
+
519
+ var columnId = headerColumnId(header);
520
+ if (!columnId) return;
521
+
522
+ event.preventDefault();
523
+ event.stopPropagation();
524
+
525
+ var sourceTable = sourceTableForHandle(handle);
526
+ var key = storageKeyForTable(sourceTable || handle.closest(TABLE_SELECTOR));
527
+ var widths = safeReadWidths(key);
528
+ delete widths[columnId];
529
+ safeWriteWidths(key, widths);
530
+ clearColumnWidth(columnId, key);
531
+ scheduleStickyRefresh(true);
532
+ }
533
+
534
+ function stopHandleClick(event) {
535
+ event.preventDefault();
536
+ event.stopPropagation();
537
+ }
538
+
539
+ function ensureHandle(header) {
540
+ if (!headerColumnId(header)) return;
541
+
542
+ header.classList.add(HEADER_CLASS);
543
+
544
+ var handle = Array.from(header.children).find(function(child) {
545
+ return child.classList && child.classList.contains(HANDLE_CLASS);
546
+ });
547
+
548
+ if (!handle) {
549
+ handle = document.createElement('span');
550
+ handle.className = HANDLE_CLASS;
551
+ handle.setAttribute('aria-hidden', 'true');
552
+ header.appendChild(handle);
553
+ }
554
+
555
+ if (initializedHandles.has(handle)) return;
556
+
557
+ initializedHandles.add(handle);
558
+ handle.addEventListener('pointerdown', startDrag);
559
+ handle.addEventListener('dblclick', resetColumn);
560
+ handle.addEventListener('click', stopHandleClick);
561
+ }
562
+
563
+ function initializeTable(table) {
564
+ return configureTable(table);
565
+ }
566
+
567
+ function initializeColumnResizer(root) {
568
+ if (!root.querySelectorAll) return;
569
+
570
+ var configuredTables = tablesFromRoot(root).filter(function(table) {
571
+ return !!initializeTable(table);
572
+ });
573
+
574
+ applyStoredWidthsToTables(configuredTables);
575
+ }
576
+
577
+ function initializeFromDocument() {
578
+ initializeColumnResizer(document);
579
+ }
580
+
581
+ if (document.readyState === 'loading') {
582
+ document.addEventListener('DOMContentLoaded', initializeFromDocument);
583
+ } else {
584
+ initializeFromDocument();
585
+ }
586
+
587
+ document.addEventListener('turbo:load', initializeFromDocument);
588
+ window.addEventListener('resize', initializeFromDocument);
589
+
590
+ if (window.MutationObserver) {
591
+ var mutationObserver = new MutationObserver(function(mutations) {
592
+ mutations.forEach(function(mutation) {
593
+ mutation.addedNodes.forEach(function(node) {
594
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
595
+ if (!shouldInitializeForAddedNode(node)) return;
596
+
597
+ initializeColumnResizer(node);
598
+ });
599
+ });
600
+ });
601
+
602
+ mutationObserver.observe(document.documentElement, {
603
+ childList: true,
604
+ subtree: true
605
+ });
606
+ }
607
+
608
+ setTimeout(initializeFromDocument, 100);
609
+ setTimeout(initializeFromDocument, 300);
610
+ })();
@@ -1,5 +1,6 @@
1
1
  (function() {
2
2
  var TABLE_SELECTOR = "table[data-fixed-columns-count]";
3
+ var MOBILE_MEDIA_QUERY = "(max-width: 767px)";
3
4
  var resizeObservers = new WeakMap();
4
5
 
5
6
  function directCells(row) {
@@ -31,10 +32,20 @@
31
32
  }
32
33
 
33
34
  function fixedColumnsCount(table) {
34
- var parsedCount = parseInt(table.dataset.fixedColumnsCount || "0", 10);
35
+ var rawCount = table.dataset.fixedColumnsCount || "0";
36
+
37
+ if (isMobile() && table.dataset.mobileFixedColumnsCount) {
38
+ rawCount = table.dataset.mobileFixedColumnsCount;
39
+ }
40
+
41
+ var parsedCount = parseInt(rawCount, 10);
35
42
  return Number.isNaN(parsedCount) ? 0 : Math.max(parsedCount, 0);
36
43
  }
37
44
 
45
+ function isMobile() {
46
+ return window.matchMedia && window.matchMedia(MOBILE_MEDIA_QUERY).matches;
47
+ }
48
+
38
49
  function stickyOffsets(table, count) {
39
50
  var headerRow = table.querySelector("thead tr");
40
51
  if (!headerRow) return [];
@@ -1,6 +1,7 @@
1
1
  (function() {
2
2
  var TABLE_SELECTOR = 'table[data-fixed-columns-count]';
3
3
  var WRAPPER_SELECTOR = '[data-fixed-header-scroll], .scroll-table, .home-table__wrapper, .table-wrap, .af__table__content';
4
+ var MOBILE_MEDIA_QUERY = '(max-width: 767px)';
4
5
  var resizeObservers = new WeakMap();
5
6
  var observedScrolls = new WeakMap();
6
7
  var observedFixedScrolls = new WeakMap();
@@ -36,10 +37,20 @@
36
37
  }
37
38
 
38
39
  function fixedColumnsCount(table) {
39
- var parsedCount = parseInt(table.dataset.fixedColumnsCount || '0', 10);
40
+ var rawCount = table.dataset.fixedColumnsCount || '0';
41
+
42
+ if (isMobile() && table.dataset.mobileFixedColumnsCount) {
43
+ rawCount = table.dataset.mobileFixedColumnsCount;
44
+ }
45
+
46
+ var parsedCount = parseInt(rawCount, 10);
40
47
  return Number.isNaN(parsedCount) ? 0 : Math.max(parsedCount, 0);
41
48
  }
42
49
 
50
+ function isMobile() {
51
+ return window.matchMedia && window.matchMedia(MOBILE_MEDIA_QUERY).matches;
52
+ }
53
+
43
54
  function stickyOffsets(widths) {
44
55
  var offsets = [];
45
56
  var currentLeft = 0;
@@ -555,6 +566,7 @@
555
566
 
556
567
  fixedTable.className = fixedHeaderTableClassName(sourceTable);
557
568
  fixedTable.dataset.fixedColumnsCount = sourceTable.dataset.fixedColumnsCount || '0';
569
+ fixedTable.dataset.mobileFixedColumnsCount = sourceTable.dataset.mobileFixedColumnsCount || fixedTable.dataset.fixedColumnsCount;
558
570
  fixedTable.style.width = '';
559
571
  fixedTable.innerHTML = sourceHead.outerHTML;
560
572
 
@@ -0,0 +1,64 @@
1
+ .admin-column-resizer__header {
2
+ position: relative;
3
+ }
4
+
5
+ .admin-column-resizer__handle {
6
+ position: absolute;
7
+ top: 0;
8
+ right: -5px;
9
+ bottom: 0;
10
+ z-index: 20;
11
+ width: 12px;
12
+ cursor: col-resize;
13
+ touch-action: none;
14
+ }
15
+
16
+ .admin-column-resizer__handle::after {
17
+ content: "";
18
+ position: absolute;
19
+ top: 20%;
20
+ right: 3px;
21
+ bottom: 20%;
22
+ width: 1px;
23
+ background: rgba(255, 255, 255, 0.45);
24
+ opacity: 0;
25
+ transition: opacity 0.15s ease, background-color 0.15s ease;
26
+ }
27
+
28
+ .admin-column-resizer__header:hover > .admin-column-resizer__handle::after,
29
+ .admin-column-resizer__handle:focus-visible::after,
30
+ .admin-column-resizer--dragging .admin-column-resizer__handle::after {
31
+ opacity: 1;
32
+ }
33
+
34
+ .admin-column-resizer__handle:hover::after,
35
+ .admin-column-resizer__handle:focus-visible::after {
36
+ background: rgba(255, 255, 255, 0.8);
37
+ }
38
+
39
+ .admin-column-resizer--dragging,
40
+ .admin-column-resizer--dragging * {
41
+ cursor: col-resize !important;
42
+ user-select: none !important;
43
+ }
44
+
45
+ [data-fixed-table-header] .admin-column-resizer__handle {
46
+ pointer-events: auto;
47
+ }
48
+
49
+ [data-fixed-table-header] .table-fixed-header__table th.admin-column-resizer__header:not(.sticky):not(.sticky-left) {
50
+ position: relative;
51
+ }
52
+
53
+ @media (pointer: coarse), screen and (max-width: 767px) {
54
+ .admin-column-resizer__handle {
55
+ right: -10px;
56
+ width: 28px;
57
+ }
58
+
59
+ .admin-column-resizer__handle::after {
60
+ right: 13px;
61
+ background: rgba(255, 255, 255, 0.75);
62
+ opacity: 1;
63
+ }
64
+ }
@@ -48,7 +48,7 @@
48
48
  z-index: 2;
49
49
 
50
50
  &::after {
51
- background-color: transparent;
51
+ background-color: #d1d5db;
52
52
  bottom: 0;
53
53
  content: "";
54
54
  left: 3px;
@@ -59,10 +59,14 @@
59
59
  }
60
60
 
61
61
  &:hover::after {
62
- background-color: #9aa8ba;
62
+ background-color: #4b5563;
63
63
  }
64
64
  }
65
65
 
66
+ .admin-navigation--resizing .admin-navigation-resize-handle::after {
67
+ background-color: #374151;
68
+ }
69
+
66
70
  .admin-navigation-scroll-area {
67
71
  max-height: inherit;
68
72
  overflow-x: hidden;
@@ -70,10 +74,6 @@
70
74
  overscroll-behavior: contain;
71
75
  }
72
76
 
73
- .admin-navigation--resizing .admin-navigation-resize-handle::after {
74
- background-color: #5d7596;
75
- }
76
-
77
77
  .admin-navigation-scroll-area .menubar,
78
78
  .admin-navigation-scroll-area .menubar a,
79
79
  .admin-navigation-scroll-area .menubar__group-label,
@@ -99,6 +99,10 @@
99
99
  width: 100%;
100
100
  }
101
101
 
102
+ .admin-navigation-scroll-area .navigation__link--indented {
103
+ text-indent: 16px;
104
+ }
105
+
102
106
  .admin-navigation-scroll-area .menubar li.menubar__item a {
103
107
  padding-left: 36px;
104
108
  }
@@ -1,5 +1,6 @@
1
1
  @import "fixed_submit_actions";
2
2
  @import "datetime_input";
3
+ @import "column_resizer";
3
4
 
4
5
  @media screen and (min-width: 768px) {
5
6
  .app-container {
@@ -661,6 +662,27 @@ table[data-fixed-columns-count] [data-fixed-header-column-min-width] {
661
662
  }
662
663
 
663
664
  @media (max-width: 767px) {
665
+ .scroll-table,
666
+ .sticky-table-scroll,
667
+ .home-table__wrapper,
668
+ .table-wrap,
669
+ .af__table__content {
670
+ max-width: 100%;
671
+ overflow-x: auto;
672
+ overflow-y: hidden;
673
+ -webkit-overflow-scrolling: touch;
674
+ overscroll-behavior-x: contain;
675
+ touch-action: pan-x pan-y;
676
+ }
677
+
678
+ .scroll-table table[data-fixed-columns-count],
679
+ .sticky-table-scroll table[data-fixed-columns-count],
680
+ .home-table__wrapper table[data-fixed-columns-count],
681
+ .table-wrap table[data-fixed-columns-count],
682
+ .af__table__content table[data-fixed-columns-count] {
683
+ min-width: max-content;
684
+ }
685
+
664
686
  .yummy-guide-administrate-filter-form__title {
665
687
  padding: 16px 16px 12px;
666
688
  }
@@ -25,6 +25,18 @@ module YummyGuide
25
25
  end
26
26
  end
27
27
 
28
+ def self.index_mobile_fixed_columns_count
29
+ if const_defined?(:INDEX_MOBILE_FIXED_COLUMNS_COUNT, false)
30
+ const_get(:INDEX_MOBILE_FIXED_COLUMNS_COUNT)
31
+ elsif const_defined?(:INDEX_FIXED_COLUMNS_COUNT, false)
32
+ [index_fixed_columns_count.to_i, 1].min
33
+ elsif superclass.respond_to?(:index_mobile_fixed_columns_count)
34
+ superclass.index_mobile_fixed_columns_count
35
+ else
36
+ [index_fixed_columns_count.to_i, 1].min
37
+ end
38
+ end
39
+
28
40
  def self.collection_sortable_attributes
29
41
  if const_defined?(:COLLECTION_SORTABLE_ATTRIBUTES, false)
30
42
  const_get(:COLLECTION_SORTABLE_ATTRIBUTES)
@@ -43,4 +55,3 @@ module YummyGuide
43
55
  end
44
56
  end
45
57
  end
46
-
@@ -9,20 +9,34 @@ module YummyGuide
9
9
  ].freeze
10
10
 
11
11
  def yummy_guide_administrate_collection_table_fixed_columns_count(page:, collection_presenter:)
12
- return 0 unless page.respond_to?(:instance_variable_defined?) && page.instance_variable_defined?(:@dashboard)
13
-
14
- dashboard = page.instance_variable_get(:@dashboard)
15
- return 0 unless dashboard&.class&.respond_to?(:index_fixed_columns_count)
16
-
17
- fixed_columns_count = dashboard.class.index_fixed_columns_count.to_i
18
- fixed_columns_count = 0 if fixed_columns_count.negative?
12
+ yummy_guide_administrate_collection_fixed_columns_count_for(
13
+ page: page,
14
+ collection_presenter: collection_presenter,
15
+ method_name: :index_fixed_columns_count
16
+ )
17
+ end
19
18
 
20
- attribute_count = collection_presenter.attribute_types.size
21
- [fixed_columns_count, attribute_count].min
19
+ def yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page:, collection_presenter:)
20
+ yummy_guide_administrate_collection_fixed_columns_count_for(
21
+ page: page,
22
+ collection_presenter: collection_presenter,
23
+ method_name: :index_mobile_fixed_columns_count
24
+ )
22
25
  rescue NoMethodError
23
26
  0
24
27
  end
25
28
 
29
+ def yummy_guide_administrate_collection_column_id(collection_presenter, column_name)
30
+ [
31
+ collection_presenter.resource_name,
32
+ column_name
33
+ ].map { |segment| segment.to_s.parameterize(separator: "_") }.join(".")
34
+ end
35
+
36
+ def yummy_guide_administrate_collection_actions_column_id(collection_presenter)
37
+ yummy_guide_administrate_collection_column_id(collection_presenter, :actions)
38
+ end
39
+
26
40
  def yummy_guide_administrate_build_collection_cell(content:, present_path: nil, target: nil, reference_link: false, text_link: false, leading_actions: nil, copy_text: nil, copy_text_transform: nil)
27
41
  normalized_content = yummy_guide_administrate_collection_cell_content(content)
28
42
 
@@ -87,6 +101,21 @@ module YummyGuide
87
101
 
88
102
  private
89
103
 
104
+ def yummy_guide_administrate_collection_fixed_columns_count_for(page:, collection_presenter:, method_name:)
105
+ return 0 unless page.respond_to?(:instance_variable_defined?) && page.instance_variable_defined?(:@dashboard)
106
+
107
+ dashboard = page.instance_variable_get(:@dashboard)
108
+ return 0 unless dashboard&.class&.respond_to?(method_name)
109
+
110
+ fixed_columns_count = dashboard.class.public_send(method_name).to_i
111
+ fixed_columns_count = 0 if fixed_columns_count.negative?
112
+
113
+ attribute_count = collection_presenter.attribute_types.size
114
+ [fixed_columns_count, attribute_count].min
115
+ rescue NoMethodError
116
+ 0
117
+ end
118
+
90
119
  def yummy_guide_administrate_collection_link(content, href:, target: nil, html_class: "action-show", aria: nil, data: nil)
91
120
  return content if href.blank?
92
121
 
@@ -17,7 +17,8 @@ module YummyGuide
17
17
  extra_actions: [],
18
18
  button_label: "Filter",
19
19
  title: "Filter Options",
20
- submit_label: "Filter"
20
+ submit_label: "Filter",
21
+ close_label: "Close"
21
22
  )
22
23
  dashboard ||= admin_filter_dashboard_from_page(page)
23
24
  scope = form.to_s
@@ -63,7 +64,8 @@ module YummyGuide
63
64
  locals: locals,
64
65
  extra_actions: extra_actions,
65
66
  title: title,
66
- submit_label: submit_label
67
+ submit_label: submit_label,
68
+ close_label: close_label
67
69
  )
68
70
  ])
69
71
  end
@@ -104,7 +106,7 @@ module YummyGuide
104
106
  .select { |field| field.visible?(self, locals) }
105
107
  end
106
108
 
107
- def admin_filter_form(fields:, scope:, path:, clear_path:, method:, current_values:, hidden_fields:, root_hidden_fields:, locals:, extra_actions:, title:, submit_label:)
109
+ def admin_filter_form(fields:, scope:, path:, clear_path:, method:, current_values:, hidden_fields:, root_hidden_fields:, locals:, extra_actions:, title:, submit_label:, close_label:)
108
110
  form_with(
109
111
  url: path,
110
112
  scope: scope,
@@ -119,7 +121,7 @@ module YummyGuide
119
121
  ) do |f|
120
122
  safe_join([
121
123
  admin_filter_hidden_fields(scope, hidden_fields, root_hidden_fields),
122
- content_tag(:h2, title, class: "filter-form__title"),
124
+ admin_filter_form_header(title: title, close_label: close_label),
123
125
  content_tag(:div, class: "filter-form__body") do
124
126
  content_tag(:table, class: "filter_table") do
125
127
  safe_join(fields.map { |field| field.row(self, f, scope, current_values, locals) })
@@ -136,6 +138,22 @@ module YummyGuide
136
138
  end
137
139
  end
138
140
 
141
+ def admin_filter_form_header(title:, close_label:)
142
+ content_tag(:div, class: "filter-form__header") do
143
+ safe_join([
144
+ content_tag(:h2, title, class: "filter-form__title"),
145
+ button_tag(
146
+ "x",
147
+ type: "button",
148
+ class: "filter-form__close",
149
+ data: { behavior: "filter-form-close" },
150
+ aria: { label: close_label },
151
+ title: close_label
152
+ )
153
+ ])
154
+ end
155
+ end
156
+
139
157
  def admin_filter_hidden_fields(scope, hidden_fields, root_hidden_fields)
140
158
  root_tags = admin_filter_evaluated_hash(root_hidden_fields).map do |key, value|
141
159
  hidden_field_tag(key, value)
@@ -1,7 +1,12 @@
1
+ <% column_width_storage_scope = local_assigns[:column_width_storage_scope].presence %>
1
2
  <div class="scroll-table" data-fixed-header-scroll>
2
3
  <table
3
4
  aria-labelledby="<%= table_title %>"
4
5
  data-fixed-columns-count="<%= yummy_guide_administrate_collection_table_fixed_columns_count(page: page, collection_presenter: collection_presenter) %>"
6
+ data-mobile-fixed-columns-count="<%= yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page: page, collection_presenter: collection_presenter) %>"
7
+ <% if column_width_storage_scope %>
8
+ data-column-resizer-storage-scope="<%= column_width_storage_scope %>"
9
+ <% end %>
5
10
  data-fixed-header-source
6
11
  >
7
12
  <thead>
@@ -11,6 +16,7 @@
11
16
  class="cell-label cell-label--<%= attr_type.html_class %> cell-label--<%= collection_presenter.ordered_html_class(attr_name) %> cell-label--<%= "#{collection_presenter.resource_name}_#{attr_name}" %>"
12
17
  scope="col"
13
18
  aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>"
19
+ data-column-id="<%= yummy_guide_administrate_collection_column_id(collection_presenter, attr_name) %>"
14
20
  >
15
21
  <%= link_to(sanitized_order_params(page, collection_field_name).merge(
16
22
  collection_presenter.order_params_for(attr_name, key: collection_field_name)
@@ -9,6 +9,7 @@ module YummyGuide
9
9
  app.config.assets.precompile += %w[
10
10
  yummy_guide_administrate/components.css
11
11
  yummy_guide_administrate/clipboards.js
12
+ yummy_guide_administrate/column_resizer.js
12
13
  yummy_guide_administrate/datetime_input.js
13
14
  yummy_guide_administrate/fixed_submit_actions.js
14
15
  yummy_guide_administrate/filter_controls.js
@@ -2,6 +2,6 @@
2
2
 
3
3
  module YummyGuide
4
4
  module Administrate
5
- VERSION = "0.7.0"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
@@ -18,6 +18,30 @@ RSpec.describe YummyGuide::Administrate::ApplicationDashboard do
18
18
  end
19
19
  end
20
20
 
21
+ describe ".index_mobile_fixed_columns_count" do
22
+ it "uses the subclass mobile constant when defined" do
23
+ subclass = Class.new(described_class)
24
+ subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 5)
25
+ subclass.const_set(:INDEX_MOBILE_FIXED_COLUMNS_COUNT, 2)
26
+
27
+ expect(subclass.index_mobile_fixed_columns_count).to eq(2)
28
+ end
29
+
30
+ it "caps a subclass desktop fixed column count to one by default" do
31
+ subclass = Class.new(described_class)
32
+ subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 5)
33
+
34
+ expect(subclass.index_mobile_fixed_columns_count).to eq(1)
35
+ end
36
+
37
+ it "keeps zero fixed columns on mobile when desktop fixed columns are zero" do
38
+ subclass = Class.new(described_class)
39
+ subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 0)
40
+
41
+ expect(subclass.index_mobile_fixed_columns_count).to eq(0)
42
+ end
43
+ end
44
+
21
45
  describe ".collection_attribute_sortable?" do
22
46
  it "uses collection attributes when explicit sortable attributes are absent" do
23
47
  subclass = Class.new(described_class)
@@ -37,6 +37,46 @@ RSpec.describe YummyGuide::Administrate::CollectionHelper do
37
37
  end
38
38
  end
39
39
 
40
+ describe "#yummy_guide_administrate_collection_table_mobile_fixed_columns_count" do
41
+ # dashboard のモバイル固定列数も表示列数を超えないことを確認する
42
+ it "caps the mobile fixed column count by the number of visible attributes" do
43
+ dashboard_class = Class.new do
44
+ def self.index_mobile_fixed_columns_count
45
+ 4
46
+ end
47
+ end
48
+
49
+ page = Object.new
50
+ page.instance_variable_set(:@dashboard, dashboard_class.new)
51
+ collection_presenter = Struct.new(:attribute_types).new({ id: :integer, name: :string })
52
+
53
+ expect(helper_host.yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page: page, collection_presenter: collection_presenter)).to eq(2)
54
+ end
55
+ end
56
+
57
+ describe "#yummy_guide_administrate_collection_column_id" do
58
+ let(:collection_presenter) { Struct.new(:resource_name).new("external_reservation") }
59
+
60
+ # 一覧カラム幅の保存に使う ID が列順ではなくリソース名と属性名から決まることを確認する
61
+ it "builds a stable id from the resource name and attribute name" do
62
+ expect(helper_host.yummy_guide_administrate_collection_column_id(collection_presenter, :customer_name)).to eq("external_reservation.customer_name")
63
+ end
64
+
65
+ # 手書き追加カラムも同じ形式の ID で保存対象にできることを確認する
66
+ it "normalizes custom column names" do
67
+ expect(helper_host.yummy_guide_administrate_collection_column_id(collection_presenter, "Article Pages")).to eq("external_reservation.article_pages")
68
+ end
69
+ end
70
+
71
+ describe "#yummy_guide_administrate_collection_actions_column_id" do
72
+ # actions 列も通常カラムと同じ保存キー体系に入ることを確認する
73
+ it "builds the actions column id" do
74
+ collection_presenter = Struct.new(:resource_name).new("reservation")
75
+
76
+ expect(helper_host.yummy_guide_administrate_collection_actions_column_id(collection_presenter)).to eq("reservation.actions")
77
+ end
78
+ end
79
+
40
80
  describe "#yummy_guide_administrate_build_collection_cell" do
41
81
  let(:present_path) { "/admin/articles/test-article" }
42
82
 
@@ -42,8 +42,12 @@ RSpec.describe YummyGuide::Administrate::FilterControlsHelper do
42
42
  search_options: { keyword: "tokyo", status: "closed" }
43
43
  )
44
44
  document = fragment(html)
45
+ close_button = document.at_css('button.filter-form__close[data-behavior="filter-form-close"]')
45
46
 
46
47
  expect(document.at_css("#reserv-filter-options > a.button").text).to eq("Filter")
48
+ expect(document.at_css(".filter-form__header .filter-form__title").text).to eq("Filter Options")
49
+ expect(close_button["aria-label"]).to eq("Close")
50
+ expect(close_button["type"]).to eq("button")
47
51
  expect(document.at_css("form.filter-form")["action"]).to eq("/admin/resources")
48
52
  expect(document.at_css('input[name="search_options[keyword]"]')["value"]).to eq("tokyo")
49
53
  expect(document.at_css('select[name="search_options[status]"] option[selected]')["value"]).to eq("closed")
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.7.0
4
+ version: 0.8.0
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-05-29 00:00:00.000000000 Z
11
+ date: 2026-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: administrate
@@ -91,6 +91,7 @@ files:
91
91
  - Rakefile
92
92
  - app/assets/images/yummy_guide_administrate/icon-copy.svg
93
93
  - app/assets/javascripts/yummy_guide_administrate/clipboards.js
94
+ - app/assets/javascripts/yummy_guide_administrate/column_resizer.js
94
95
  - app/assets/javascripts/yummy_guide_administrate/datetime_input.js
95
96
  - app/assets/javascripts/yummy_guide_administrate/filter_controls.js
96
97
  - app/assets/javascripts/yummy_guide_administrate/filter_form.js
@@ -98,6 +99,7 @@ files:
98
99
  - app/assets/javascripts/yummy_guide_administrate/resizable_navigation.js
99
100
  - app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js
100
101
  - app/assets/javascripts/yummy_guide_administrate/sticky_table_headers.js
102
+ - app/assets/stylesheets/yummy_guide_administrate/_column_resizer.scss
101
103
  - app/assets/stylesheets/yummy_guide_administrate/_datetime_input.scss
102
104
  - app/assets/stylesheets/yummy_guide_administrate/_fixed_submit_actions.scss
103
105
  - app/assets/stylesheets/yummy_guide_administrate/_resizable_navigation.scss