(function (app) { 'use strict'; // Sort state is an ordered list of {field, dir} keys; the first is // primary, additional keys break ties. function defaultsFromContext(ctx) { const defaults = ctx.defaults || {}; if (Array.isArray(defaults.sort) && defaults.sort.length > 0) { return defaults.sort.slice(); } // Fall back to any column with `sort:` set. const fromCols = (ctx.columns || []).filter(function (c) { return c.sort; }); if (fromCols.length > 0) { return fromCols.map(function (c) { const dir = c.sort === 'desc' ? 'desc' : 'asc'; return { field: c.field, dir: dir }; }); } return []; } function findColumn(columns, field) { for (let i = 0; i < columns.length; i++) { if (columns[i].field === field) { return columns[i]; } } return null; } // Click handler for a header: cycle the sort state for `field`. // - Not currently a sort key → add as primary, asc // - Currently primary asc → flip to desc // - Currently primary desc → remove // - Currently secondary → promote to primary, asc // Shift-click is meant for additional accumulation but we keep the // single-click semantics simple; advanced multi-sort can be a // follow-up. function cycle(state, field, multi) { const idx = state.findIndex(function (s) { return s.field === field; }); if (multi) { if (idx === -1) { return state.concat([{ field: field, dir: 'asc' }]); } const cur = state[idx]; if (cur.dir === 'asc') { const next = state.slice(); next[idx] = { field: field, dir: 'desc' }; return next; } return state.slice(0, idx).concat(state.slice(idx + 1)); } if (idx === -1) { return [{ field: field, dir: 'asc' }]; } if (idx === 0) { const cur = state[0]; if (cur.dir === 'asc') { return [{ field: field, dir: 'desc' }]; } return []; } return [{ field: field, dir: 'asc' }]; } function apply(rows, sortState, columns, util) { if (!sortState || sortState.length === 0) { return rows; } const out = rows.slice(); out.sort(function (a, b) { for (let i = 0; i < sortState.length; i++) { const key = sortState[i]; const col = findColumn(columns, key.field); const fmt = col ? col.format : ''; const av = util.resolveField(a.data, key.field); const bv = util.resolveField(b.data, key.field); const cmp = util.compareCells(av, bv, fmt); if (cmp !== 0) { return key.dir === 'desc' ? -cmp : cmp; } } return 0; }); return out; } function indicator(sortState, field) { for (let i = 0; i < sortState.length; i++) { if (sortState[i].field === field) { const arrow = sortState[i].dir === 'desc' ? ' ▼' : ' ▲'; if (sortState.length > 1) { return arrow + (i + 1); } return arrow; } } return ''; } app.modules.sort = { defaultsFromContext: defaultsFromContext, cycle: cycle, apply: apply, indicator: indicator }; })(window.tablesApp);