Sync styleguide 2026.05.18.2
This commit is contained in:
1 parent
ca6015024b
commit
900e7dee1c
3 files changed
+263
-3
No files matched your search
@@ -7,6 +7,7 @@ Der Styleguide bleibt in diesem Repository die Source of Truth. Der Sync verteil
|
||||
- den deploy-relevanten CSS-Upstream versioniert als `public/assets/styleguide.upstream.css`
|
||||
- die vollstaendige Styleguide-Dokumentation gespiegelt nach `docs/styleguide`
|
||||
- die portal-spezifische Fertig-CSS-Datei als `public/assets/styles.css`
|
||||
- die konsumierbare Large-Table-Runtime als `public/scripts/large-table-column-resize.js`
|
||||
|
||||
Zielrepos:
|
||||
|
||||
@@ -31,8 +32,10 @@ Beispiel:
|
||||
|
||||
- `public/assets/styleguide.upstream.css` aktualisiert
|
||||
- `public/assets/styleguide.upstream.meta.json` aktualisiert (Version, Commit, Zeitstempel)
|
||||
- `docs/styleguide` gespiegelt (mit `--delete`, ohne `.git`, `.codex`, `AGENTS.md`, `scripts/`)
|
||||
- `docs/styleguide` gespiegelt (mit `--delete`, ohne `.git`, `.codex`, `AGENTS.md` und interne `scripts/`)
|
||||
- `public/scripts/large-table-column-resize.js` aus der Styleguide-Source-of-Truth kopiert
|
||||
- `public/assets/styles.css` aktualisiert
|
||||
- `public/scripts/large-table-column-resize.js` aktualisiert; lokale Portal-Kopien sind nicht zulässig
|
||||
- Optional: automatischer Commit + Push im Portalrepo
|
||||
|
||||
## Standardprozess je Release
|
||||
@@ -42,3 +45,18 @@ Beispiel:
|
||||
3. Styleguide commit + push
|
||||
4. Sync-Skript ausfuehren
|
||||
5. Beide Portalrepos Smoke-Testen
|
||||
|
||||
## Large-Table-Runtime
|
||||
|
||||
Die Runtime liegt ausschließlich im Styleguide unter `scripts/large-table-column-resize.js`.
|
||||
Der Sync kopiert nur diese explizit freigegebene Runtime nach `public/scripts/`; andere
|
||||
Styleguide-Scripts wie Prüf- und Sync-Shellscripts werden nicht in `docs/styleguide/scripts/`
|
||||
gespiegelt. Dadurch bleibt der Styleguide die Source of Truth und ein erneuter Sync
|
||||
überschreibt die Portaldatei kontrolliert.
|
||||
|
||||
Für den Smoke-Test in beiden Portalen ist zu prüfen:
|
||||
|
||||
- `public/scripts/large-table-column-resize.js` ist vorhanden und unter `/scripts/large-table-column-resize.js` ladbar.
|
||||
- Die Large-Table-Seite lädt die Runtime ohne lokale Inline- oder Duplikatimplementierung.
|
||||
- Nach einer Spaltenbreitenänderung wird der Eintrag unter `sg:large-table:column-widths:<key>` gespeichert.
|
||||
- Ein erneuter Sync ersetzt die Datei aus der Styleguide-Quelle.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"styleguideVersion": "2026.05.18.2",
|
||||
"styleguideCommit": "5134b61",
|
||||
"syncedAtUtc": "2026-09-27T06:20:05Z",
|
||||
"styleguideCommit": "09966ce",
|
||||
"syncedAtUtc": "2026-09-27T06:25:30Z",
|
||||
"sourceRepo": "/Users/mathias/Documents/Dokumente Chouchou/Codebases/Styleguide",
|
||||
"mirroredDocsPath": "docs/styleguide"
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
(function initLargeTableColumnResizeModule() {
|
||||
const STORAGE_PREFIX = 'sg:large-table:column-widths:';
|
||||
const STORAGE_VERSION = 1;
|
||||
const DEFAULT_MIN_WIDTH = 128;
|
||||
const KEYBOARD_STEP = 8;
|
||||
const KEYBOARD_LARGE_STEP = 40;
|
||||
let activeResize = null;
|
||||
|
||||
const getTables = (root) => root.querySelectorAll('[data-component="large-table"][data-column-resize="true"]');
|
||||
|
||||
const getColumnHeaders = (table) => Array.from(
|
||||
table.querySelectorAll('[data-component-part="large-table-header-row"] [role="columnheader"]'),
|
||||
);
|
||||
|
||||
const getMinWidth = (table) => {
|
||||
const configured = table.dataset.columnMinWidth;
|
||||
const value = configured || getComputedStyle(table).getPropertyValue('--large-table-column-min-width').trim();
|
||||
if (!value) {
|
||||
return DEFAULT_MIN_WIDTH;
|
||||
}
|
||||
|
||||
const probe = document.createElement('span');
|
||||
probe.style.cssText = `position:absolute;width:${value};visibility:hidden;`;
|
||||
table.appendChild(probe);
|
||||
const width = probe.getBoundingClientRect().width;
|
||||
probe.remove();
|
||||
return width > 0 ? width : DEFAULT_MIN_WIDTH;
|
||||
};
|
||||
|
||||
const parseDefaultWidth = (rawValue, tableWidth) => {
|
||||
if (!rawValue) {
|
||||
return null;
|
||||
}
|
||||
const value = Number.parseFloat(rawValue);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return null;
|
||||
}
|
||||
if (rawValue.trim().endsWith('%')) {
|
||||
return tableWidth * value / 100;
|
||||
}
|
||||
if (rawValue.trim().endsWith('fr')) {
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const normalizeWidths = (widths, tableWidth, minWidth) => {
|
||||
const safeWidths = widths.map((width) => Math.max(minWidth, width));
|
||||
const total = safeWidths.reduce((sum, width) => sum + width, 0);
|
||||
if (total <= 0) {
|
||||
return Array(widths.length).fill(Math.max(minWidth, tableWidth / widths.length));
|
||||
}
|
||||
if (widths.some((width) => width !== width)) {
|
||||
return Array(widths.length).fill(Math.max(minWidth, tableWidth / widths.length));
|
||||
}
|
||||
return safeWidths.map((width) => width * tableWidth / total);
|
||||
};
|
||||
|
||||
const getDefaultWidths = (table, headers, minWidth) => {
|
||||
const tableWidth = Math.max(table.clientWidth, minWidth * headers.length);
|
||||
const configured = headers.map((header) => parseDefaultWidth(header.dataset.defaultWidth, tableWidth));
|
||||
if (configured.some((width) => width === null)) {
|
||||
return Array(headers.length).fill(Math.max(minWidth, tableWidth / headers.length));
|
||||
}
|
||||
return normalizeWidths(configured, tableWidth, minWidth);
|
||||
};
|
||||
|
||||
const getStorageKey = (table) => `${STORAGE_PREFIX}${table.dataset.columnResizeKey || ''}`;
|
||||
|
||||
const readStoredWidths = (table, count, minWidth) => {
|
||||
const key = getStorageKey(table);
|
||||
if (!table.dataset.columnResizeKey) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (!window.localStorage) {
|
||||
return null;
|
||||
}
|
||||
const stored = JSON.parse(window.localStorage.getItem(key));
|
||||
if (stored?.version !== STORAGE_VERSION || !Array.isArray(stored.widths) || stored.widths.length !== count) {
|
||||
return null;
|
||||
}
|
||||
if (stored.widths.some((width) => !Number.isFinite(width) || width < minWidth)) {
|
||||
return null;
|
||||
}
|
||||
return stored.widths;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const writeStoredWidths = (table, widths) => {
|
||||
if (!table.dataset.columnResizeKey) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!window.localStorage) {
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(getStorageKey(table), JSON.stringify({
|
||||
version: STORAGE_VERSION,
|
||||
widths: widths.map((width) => Math.round(width)),
|
||||
}));
|
||||
} catch (error) {
|
||||
// Storage can be unavailable in privacy-restricted browser contexts.
|
||||
}
|
||||
};
|
||||
|
||||
const updateHandleState = (table, widths, minWidth) => {
|
||||
const handles = table.querySelectorAll('[data-large-table-resize-handle]');
|
||||
handles.forEach((handle) => {
|
||||
const index = Number.parseInt(handle.dataset.largeTableResizeHandle, 10);
|
||||
const current = widths[index];
|
||||
const maximum = Math.max(minWidth, widths[index] + widths[index + 1] - minWidth);
|
||||
if (table.id) {
|
||||
handle.setAttribute('aria-controls', table.id);
|
||||
}
|
||||
handle.setAttribute('aria-valuemin', String(Math.round(minWidth)));
|
||||
handle.setAttribute('aria-valuemax', String(Math.round(maximum)));
|
||||
handle.setAttribute('aria-valuenow', String(Math.round(current)));
|
||||
handle.setAttribute('aria-valuetext', `${Math.round(current)} Pixel`);
|
||||
});
|
||||
};
|
||||
|
||||
const applyWidths = (table, widths, minWidth) => {
|
||||
const safeWidths = widths.map((width) => Math.max(minWidth, width));
|
||||
const total = safeWidths.reduce((sum, width) => sum + width, 0);
|
||||
table.style.setProperty('--large-table-grid-template-columns', safeWidths.map((width) => `${width}px`).join(' '));
|
||||
table.style.setProperty('--large-table-grid-min-width', `${Math.ceil(total)}px`);
|
||||
table._largeTableColumnWidths = safeWidths;
|
||||
updateHandleState(table, safeWidths, minWidth);
|
||||
};
|
||||
|
||||
const resetWidths = (table) => {
|
||||
const headers = getColumnHeaders(table);
|
||||
const minWidth = getMinWidth(table);
|
||||
const widths = getDefaultWidths(table, headers, minWidth);
|
||||
try {
|
||||
window.localStorage.removeItem(getStorageKey(table));
|
||||
} catch (error) {
|
||||
// Storage can be unavailable in privacy-restricted browser contexts.
|
||||
}
|
||||
applyWidths(table, widths, minWidth);
|
||||
};
|
||||
|
||||
const resizeColumn = (table, index, delta, persist) => {
|
||||
const widths = [...table._largeTableColumnWidths];
|
||||
const minWidth = getMinWidth(table);
|
||||
const nextLeft = Math.max(minWidth, Math.min(widths[index] + widths[index + 1] - minWidth, widths[index] + delta));
|
||||
const appliedDelta = nextLeft - widths[index];
|
||||
widths[index] = nextLeft;
|
||||
widths[index + 1] -= appliedDelta;
|
||||
applyWidths(table, widths, minWidth);
|
||||
if (persist) {
|
||||
writeStoredWidths(table, widths);
|
||||
}
|
||||
};
|
||||
|
||||
const initTable = (table) => {
|
||||
const headers = getColumnHeaders(table);
|
||||
if (headers.length < 2) {
|
||||
return;
|
||||
}
|
||||
const minWidth = getMinWidth(table);
|
||||
const stored = readStoredWidths(table, headers.length, minWidth);
|
||||
applyWidths(table, stored || getDefaultWidths(table, headers, minWidth), minWidth);
|
||||
|
||||
if (table.dataset.largeTableResizeInitialized === 'true') {
|
||||
return;
|
||||
}
|
||||
table.dataset.largeTableResizeInitialized = 'true';
|
||||
|
||||
table.addEventListener('pointerdown', (event) => {
|
||||
const handle = event.target.closest('[data-large-table-resize-handle]');
|
||||
if (!handle || !table.contains(handle)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
activeResize = {
|
||||
table,
|
||||
handle,
|
||||
index: Number.parseInt(handle.dataset.largeTableResizeHandle, 10),
|
||||
startX: event.clientX,
|
||||
startWidths: [...table._largeTableColumnWidths],
|
||||
};
|
||||
handle.dataset.largeTableResizeActive = 'true';
|
||||
table.dataset.columnResizeState = 'resizing';
|
||||
handle.setPointerCapture?.(event.pointerId);
|
||||
});
|
||||
|
||||
table.addEventListener('pointermove', (event) => {
|
||||
if (!activeResize || activeResize.table !== table) {
|
||||
return;
|
||||
}
|
||||
applyWidths(table, activeResize.startWidths, getMinWidth(table));
|
||||
resizeColumn(table, activeResize.index, event.clientX - activeResize.startX, false);
|
||||
});
|
||||
|
||||
table.addEventListener('pointerup', () => {
|
||||
if (!activeResize || activeResize.table !== table) {
|
||||
return;
|
||||
}
|
||||
writeStoredWidths(table, table._largeTableColumnWidths);
|
||||
activeResize.handle.dataset.largeTableResizeActive = 'false';
|
||||
table.dataset.columnResizeState = 'idle';
|
||||
activeResize = null;
|
||||
});
|
||||
|
||||
table.addEventListener('keydown', (event) => {
|
||||
const handle = event.target.closest('[data-large-table-resize-handle]');
|
||||
if (!handle || !table.contains(handle)) {
|
||||
return;
|
||||
}
|
||||
const index = Number.parseInt(handle.dataset.largeTableResizeHandle, 10);
|
||||
const step = event.shiftKey ? KEYBOARD_LARGE_STEP : KEYBOARD_STEP;
|
||||
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
||||
event.preventDefault();
|
||||
resizeColumn(table, index, event.key === 'ArrowRight' ? step : -step, true);
|
||||
} else if (event.key === 'Home' || event.key === 'End') {
|
||||
event.preventDefault();
|
||||
const minWidth = getMinWidth(table);
|
||||
const current = table._largeTableColumnWidths[index];
|
||||
const target = event.key === 'Home' ? minWidth : current + table._largeTableColumnWidths[index + 1] - minWidth;
|
||||
resizeColumn(table, index, target - current, true);
|
||||
}
|
||||
});
|
||||
|
||||
table.addEventListener('click', (event) => {
|
||||
const reset = event.target.closest('[data-large-table-reset-widths]');
|
||||
if (reset && table.contains(reset)) {
|
||||
resetWidths(table);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.sgInitLargeTableColumnResize = (options = {}) => {
|
||||
const root = options.root || document;
|
||||
getTables(root).forEach(initTable);
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => window.sgInitLargeTableColumnResize(), { once: true });
|
||||
})();
|
||||
Reference in new issue
Block a user