From c60bd3f737d00f1393652f3952ad6f57856046df Mon Sep 17 00:00:00 2001 From: Johannes Zillmann Date: Mon, 1 Mar 2021 23:42:02 +0100 Subject: [PATCH] Un-Grouping switch --- core/src/debug/StageResult.ts | 42 ++++++ core/src/support/groupingUtils.ts | 4 - core/test/debug/StageResults.test.ts | 207 +++++++++++++++++++++++++++ ui/src/debug/DebugView.svelte | 56 ++++++-- ui/src/debug/ItemTable.svelte | 29 ++-- ui/src/svelte/slideH.ts | 42 ++++++ 6 files changed, 343 insertions(+), 37 deletions(-) create mode 100644 core/test/debug/StageResults.test.ts create mode 100644 ui/src/svelte/slideH.ts diff --git a/core/src/debug/StageResult.ts b/core/src/debug/StageResult.ts index 0622df9..f0e02ab 100644 --- a/core/src/debug/StageResult.ts +++ b/core/src/debug/StageResult.ts @@ -4,6 +4,7 @@ import Item from '../Item'; import Page, { asPages } from './Page'; import ChangeIndex from './ChangeIndex'; import ChangeTracker from './ChangeTracker'; +import ItemGroup from './ItemGroup'; export default class StageResult { constructor( @@ -20,6 +21,47 @@ export default class StageResult { return items; }, []); } + + selectPages(relevantChangesOnly: boolean, groupItems: boolean, pinnedPage?: number): Page[] { + let result: Page[]; + + // Ungroup pages + if (!groupItems && this.descriptor?.debug?.itemMerger) { + result = this.pagesWithUnpackedItems(); + } else { + result = this.pages; + } + + // Filter to pinned page + if (Number.isInteger(pinnedPage)) { + result = result.filter((page) => page.index === pinnedPage); + } + + // Filter out item (groups) with no changes + if (relevantChangesOnly && !this.descriptor.debug?.showAll === true) { + result = result.map( + (page) => + ({ + ...page, + itemGroups: page.itemGroups.filter((itemGroup) => this.changes.hasChanged(itemGroup.top)), + } as Page), + ); + } + + return result; + } + + pagesWithUnpackedItems(): Page[] { + return this.pages.map( + (page) => + ({ + ...page, + itemGroups: new Array().concat( + ...page.itemGroups.map((itemGroup) => itemGroup.unpacked().map((item) => new ItemGroup(item))), + ), + } as Page), + ); + } } export function initialStage(inputSchema: string[], inputItems: Item[]): StageResult { diff --git a/core/src/support/groupingUtils.ts b/core/src/support/groupingUtils.ts index 553cb9f..4180f9b 100644 --- a/core/src/support/groupingUtils.ts +++ b/core/src/support/groupingUtils.ts @@ -1,8 +1,4 @@ -import ItemMerger from '../debug/ItemMerger'; import Item from '../Item'; -import ItemGroup from '../debug/ItemGroup'; -import Page from '../debug/Page'; -import ChangeTracker from '../debug/ChangeTracker'; type KeyExtractor = (item: Item) => any; type PageItemTransformer = (page: number, items: Item[]) => Item[]; diff --git a/core/test/debug/StageResults.test.ts b/core/test/debug/StageResults.test.ts new file mode 100644 index 0000000..ddafd9b --- /dev/null +++ b/core/test/debug/StageResults.test.ts @@ -0,0 +1,207 @@ +import StageResult from 'src/debug/StageResult'; +import { toDescriptor } from 'src/TransformDescriptor'; +import ChangeTracker from 'src/debug/ChangeTracker'; +import AnnotatedColumn from 'src/debug/AnnotatedColumn'; +import Page, { asPages } from 'src/debug/Page'; +import { items } from '../testItems'; +import LineItemMerger from 'src/debug/LineItemMerger'; +import ItemGroup from 'src/debug/ItemGroup'; + +test('itemsUnpacked', async () => { + const tracker = new ChangeTracker(); + const itemMerger = new LineItemMerger(false); + const descriptor = toDescriptor({ debug: { itemMerger } }); + const schema: AnnotatedColumn[] = [{ name: 'A' }]; + const flatItems = [ + ...items(0, [ + { idx: 0, line: 1 }, + { idx: 1, line: 1 }, + { idx: 2, line: 2 }, + ]), + ...items(1, [{ idx: 3, line: 1 }]), + ...items(2, [ + { idx: 4, line: 1 }, + { idx: 5, line: 1 }, + ]), + ]; + const pages = asPages(tracker, flatItems, itemMerger); + const result = new StageResult(descriptor, schema, pages, tracker, []); + + expect(result.itemsUnpacked().map((item) => item.data['idx'])).toEqual([0, 1, 2, 3, 4, 5]); +}); + +describe('select pages', () => { + function groupElements(page: Page, elementName: string) { + return page.itemGroups.map((group) => group.unpacked().map((item) => item.data['idx'])); + } + test('Changes on group level', async () => { + const tracker = new ChangeTracker(); + const itemMerger = new LineItemMerger(true); + const descriptor = toDescriptor({ debug: { itemMerger } }); + const schema: AnnotatedColumn[] = [{ name: 'A' }]; + const flatItems = [ + ...items(0, [ + { idx: 0, line: 1 }, + { idx: 1, line: 1 }, + { idx: 2, line: 2 }, + ]), + ...items(1, [{ idx: 3, line: 1 }]), + ...items(2, [ + { idx: 4, line: 1 }, + { idx: 5, line: 1 }, + ]), + ]; + const pages = asPages(tracker, flatItems, itemMerger); + const result = new StageResult(descriptor, schema, pages, tracker, []); + + const allGrouped = result.selectPages(false, true); + expect(allGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(allGrouped[0], 'idx')).toEqual([[0, 1], [2]]); + expect(groupElements(allGrouped[1], 'idx')).toEqual([[3]]); + expect(groupElements(allGrouped[2], 'idx')).toEqual([[4, 5]]); + + const relevantGrouped = result.selectPages(true, true); + expect(relevantGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantGrouped[0], 'idx')).toEqual([[0, 1]]); + expect(groupElements(relevantGrouped[1], 'idx')).toEqual([]); + expect(groupElements(relevantGrouped[2], 'idx')).toEqual([[4, 5]]); + + const relevantUnpacked = result.selectPages(true, false); + expect(relevantUnpacked.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantUnpacked[0], 'idx')).toEqual([]); + expect(groupElements(relevantUnpacked[1], 'idx')).toEqual([]); + expect(groupElements(relevantUnpacked[2], 'idx')).toEqual([]); + + const allUnpacked = result.selectPages(false, false); + expect(allUnpacked.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(allUnpacked[0], 'idx')).toEqual([[0], [1], [2]]); + expect(groupElements(allUnpacked[1], 'idx')).toEqual([[3]]); + expect(groupElements(allUnpacked[2], 'idx')).toEqual([[4], [5]]); + + const allGroupedWithPin = result.selectPages(false, true, 0); + expect(allGroupedWithPin.map((page) => page.index)).toEqual([0]); + expect(groupElements(allGroupedWithPin[0], 'idx')).toEqual([[0, 1], [2]]); + + const relevantGroupedWithPin = result.selectPages(true, true, 0); + expect(relevantGroupedWithPin.map((page) => page.index)).toEqual([0]); + expect(groupElements(relevantGroupedWithPin[0], 'idx')).toEqual([[0, 1]]); + + const relevantUnpackedWithPin = result.selectPages(true, false, 0); + expect(relevantUnpackedWithPin.map((page) => page.index)).toEqual([0]); + expect(groupElements(relevantUnpackedWithPin[0], 'idx')).toEqual([]); + + const allUnpackedWithPin = result.selectPages(false, false, 0); + expect(allUnpackedWithPin.map((page) => page.index)).toEqual([0]); + expect(groupElements(allUnpackedWithPin[0], 'idx')).toEqual([[0], [1], [2]]); + }); + + test('Changes on element level', async () => { + const tracker = new ChangeTracker(); + const itemMerger = new LineItemMerger(false); + const descriptor = toDescriptor({ debug: { itemMerger } }); + const schema: AnnotatedColumn[] = [{ name: 'A' }]; + const flatItems = [ + ...items(0, [ + { idx: 0, line: 1 }, + { idx: 1, line: 1 }, + { idx: 2, line: 2 }, + ]), + ...items(1, [{ idx: 3, line: 1 }]), + ...items(2, [ + { idx: 4, line: 1 }, + { idx: 5, line: 1 }, + { idx: 6, line: 2 }, + ]), + ]; + tracker.trackAddition(flatItems[3]); + tracker.trackAddition(flatItems[5]); + const pages = asPages(tracker, flatItems, itemMerger); + const result = new StageResult(descriptor, schema, pages, tracker, []); + + const allGrouped = result.selectPages(false, true); + expect(allGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(allGrouped[0], 'idx')).toEqual([[0, 1], [2]]); + expect(groupElements(allGrouped[1], 'idx')).toEqual([[3]]); + expect(groupElements(allGrouped[2], 'idx')).toEqual([[4, 5], [6]]); + + const relevantGrouped = result.selectPages(true, true); + expect(relevantGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantGrouped[0], 'idx')).toEqual([]); + expect(groupElements(relevantGrouped[1], 'idx')).toEqual([[3]]); + expect(groupElements(relevantGrouped[2], 'idx')).toEqual([[4, 5]]); + + const relevantUnpacked = result.selectPages(true, false); + expect(relevantUnpacked.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantUnpacked[0], 'idx')).toEqual([]); + expect(groupElements(relevantUnpacked[1], 'idx')).toEqual([[3]]); + expect(groupElements(relevantUnpacked[2], 'idx')).toEqual([[5]]); + + const allUnpacked = result.selectPages(false, false); + expect(allUnpacked.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(allUnpacked[0], 'idx')).toEqual([[0], [1], [2]]); + expect(groupElements(allUnpacked[1], 'idx')).toEqual([[3]]); + expect(groupElements(allUnpacked[2], 'idx')).toEqual([[4], [5], [6]]); + + const allGroupedWithPin = result.selectPages(false, true, 2); + expect(allGroupedWithPin.map((page) => page.index)).toEqual([2]); + expect(groupElements(allGroupedWithPin[0], 'idx')).toEqual([[4, 5], [6]]); + + const relevantGroupedWithPin = result.selectPages(true, true, 2); + expect(relevantGroupedWithPin.map((page) => page.index)).toEqual([2]); + expect(groupElements(relevantGroupedWithPin[0], 'idx')).toEqual([[4, 5]]); + + const relevantUnpackedWithPin = result.selectPages(true, false, 2); + expect(relevantUnpackedWithPin.map((page) => page.index)).toEqual([2]); + expect(groupElements(relevantUnpackedWithPin[0], 'idx')).toEqual([[5]]); + + const allUnpackedWithPin = result.selectPages(false, false, 2); + expect(allUnpackedWithPin.map((page) => page.index)).toEqual([2]); + expect(groupElements(allUnpackedWithPin[0], 'idx')).toEqual([[4], [5], [6]]); + }); + + test('showAll - grouped', async () => { + const tracker = new ChangeTracker(); + const itemMerger = new LineItemMerger(false); + const descriptor = toDescriptor({ debug: { itemMerger, showAll: true } }); + const schema: AnnotatedColumn[] = [{ name: 'A' }]; + const flatItems = [ + ...items(0, [ + { idx: 0, line: 1 }, + { idx: 1, line: 1 }, + { idx: 2, line: 2 }, + ]), + ...items(1, [{ idx: 3, line: 1 }]), + ...items(2, [ + { idx: 4, line: 1 }, + { idx: 5, line: 1 }, + ]), + ]; + const pages = asPages(tracker, flatItems, itemMerger); + const result = new StageResult(descriptor, schema, pages, tracker, []); + + const relevantGrouped = result.selectPages(true, true); + expect(relevantGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantGrouped[0], 'idx')).toEqual([[0, 1], [2]]); + expect(groupElements(relevantGrouped[1], 'idx')).toEqual([[3]]); + expect(groupElements(relevantGrouped[2], 'idx')).toEqual([[4, 5]]); + }); + + test('showAll - grouped', async () => { + const tracker = new ChangeTracker(); + const descriptor = toDescriptor({ debug: { showAll: true } }); + const schema: AnnotatedColumn[] = [{ name: 'A' }]; + const flatItems = [ + ...items(0, [{ idx: 0 }, { idx: 1 }, { idx: 2 }]), + ...items(1, [{ idx: 3 }]), + ...items(2, [{ idx: 4 }, { idx: 5 }]), + ]; + const pages = asPages(tracker, flatItems); + const result = new StageResult(descriptor, schema, pages, tracker, []); + + const relevantGrouped = result.selectPages(true, true); + expect(relevantGrouped.map((page) => page.index)).toEqual([0, 1, 2]); + expect(groupElements(relevantGrouped[0], 'idx')).toEqual([[0], [1], [2]]); + expect(groupElements(relevantGrouped[1], 'idx')).toEqual([[3]]); + expect(groupElements(relevantGrouped[2], 'idx')).toEqual([[4], [5]]); + }); +}); diff --git a/ui/src/debug/DebugView.svelte b/ui/src/debug/DebugView.svelte index 32ec1ce..049ea11 100644 --- a/ui/src/debug/DebugView.svelte +++ b/ui/src/debug/DebugView.svelte @@ -1,11 +1,13 @@
@@ -37,7 +42,7 @@
{#if pageIsPinned} - (pinnedPage = undefined)} transition:slide> + (pinnedPage = undefined)} transition:slideH={{ duration: 180, easing: linear }}> {/if} @@ -82,7 +87,16 @@
- + {#if supportsGrouping} + + + + {/if} + {#if supportsRelevanceFiltering} + + + + {/if}
@@ -97,15 +111,33 @@ - + {#if visiblePages.find((page) => page.itemGroups.length > 0)} + + {:else} +
+
No visible changes from the transformation.
+ {#if supportsRelevanceFiltering && onlyRelevantItems} +
Disabled the
+
(onlyRelevantItems = false)}> + relevance filter +
+
?
+ {/if} + {#if supportsGrouping && !groupingEnabled} +
Enable
+
(groupingEnabled = true)}> + grouping +
+
?
+ {/if} +
+ {/if}
diff --git a/ui/src/svelte/slideH.ts b/ui/src/svelte/slideH.ts new file mode 100644 index 0000000..75bd73b --- /dev/null +++ b/ui/src/svelte/slideH.ts @@ -0,0 +1,42 @@ +import { backInOut as easing } from 'svelte/easing'; + +const defaultOptions = { + delay: 0, + duration: 400, + easing, +}; + +/** + * Slide the element horizontally, across the X-axis. + * @param node + * @param options + */ +export default function slideH(node: HTMLElement, options: object) { + const mergedOptions = { ...defaultOptions, ...options }; + + const style = getComputedStyle(node); + const opacity = +style.opacity; + const width = parseFloat(style.width); + const padding_left = parseFloat(style.paddingLeft); + const padding_right = parseFloat(style.paddingRight); + const margin_left = parseFloat(style.marginLeft); + const margin_right = parseFloat(style.marginRight); + const border_left_width = parseFloat(style.borderLeftWidth); + const border_right_width = parseFloat(style.borderRightWidth); + + return { + delay: mergedOptions.delay, + duration: mergedOptions.duration, + easing: mergedOptions.easing, + css: (t: number) => + `overflow: hidden;` + + `opacity: ${Math.min(t * 20, 1) * opacity};` + + `width: ${t * width}px;` + + `padding-left: ${t * padding_left}px;` + + `padding-right: ${t * padding_right}px;` + + `margin-left: ${t * margin_left}px;` + + `margin-right: ${t * margin_right}px;` + + `border-left-width: ${t * border_left_width}px;` + + `border-right-width: ${t * border_right_width}px;`, + }; +}