Skip to content

Commit cc6b0dd

Browse files
committed
refactors, cleanups, slider toggle
1 parent 2d71e87 commit cc6b0dd

3 files changed

Lines changed: 174 additions & 126 deletions

File tree

src/lib/diff.js

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const TYPE = { UNCHANGED: 'unchanged', ADDED: 'added', REMOVED: 'removed', MODIFIED: 'modified' }
1+
const TYPE = { UNCHANGED: 'unchanged', ADDED: 'added', REMOVED: 'removed', MODIFIED: 'modified' };
22

33
const typeOf = (v) => {
44
if (v === null) return 'null';
@@ -8,9 +8,9 @@ const typeOf = (v) => {
88

99
const isObj = (v) => v !== null && typeof v === 'object';
1010

11-
const keys = (a, b) => [...new Set([...Object.keys(a || {}), ...Object.keys(b || {})])];
11+
const allKeys = (a, b) => [...new Set([...Object.keys(a || {}), ...Object.keys(b || {})])];
1212

13-
const node = (key, type, left, right, extra = {}) => ({
13+
const createNode = (key, type, left, right, extra = {}) => ({
1414
key,
1515
type,
1616
left,
@@ -19,50 +19,68 @@ const node = (key, type, left, right, extra = {}) => ({
1919
...extra
2020
});
2121

22-
const container = (val, isArr) => {
23-
if (isArr) return { isArray: true };
22+
const getContainerProps = (val) => {
23+
if (Array.isArray(val)) return { isArray: true };
2424
if (isObj(val)) return { isObject: true };
2525
return {};
2626
};
2727

28-
const childMap = (val, side) => (v, k) => node(
29-
k, TYPE.UNCHANGED,
30-
side === 'added' ? undefined : v,
31-
side === 'added' ? v : undefined,
32-
isObj(v) && { children: mapChildren(v, side), ...container(v, Array.isArray(v)) }
33-
)
28+
const mapChildrenForSide = (val, side) => {
29+
const createChildNode = (value, key) => {
30+
const leftValue = side === 'added' ? undefined : value;
31+
const rightValue = side === 'added' ? value : undefined;
32+
const childExtra = isObj(value) ? {
33+
children: mapChildrenForSide(value, side),
34+
...getContainerProps(value)
35+
} : {};
36+
return createNode(key, TYPE.UNCHANGED, leftValue, rightValue, childExtra);
37+
};
3438

35-
const mapChildren = (val, side) => {
36-
if (Array.isArray(val)) return val.map(childMap(val, side));
37-
if (isObj(val)) return Object.entries(val).map(([k, v]) => childMap(val, side)(v, k));
39+
if (Array.isArray(val)) {
40+
return val.map((item, index) => createChildNode(item, index));
41+
}
42+
if (isObj(val)) {
43+
return Object.entries(val).map(([k, v]) => createChildNode(v, k));
44+
}
3845
return [];
3946
};
4047

4148
const diffContainer = (left, right, key, isArr) => {
4249
const items = isArr
4350
? Array.from({ length: Math.max(left?.length || 0, right?.length || 0) }, (_, i) => diff(left?.[i], right?.[i], i))
44-
: keys(left, right).map(k => diff(left?.[k], right?.[k], k))
45-
const hasDiff = items.some(c => c.hasDiff)
46-
return node(key, hasDiff ? TYPE.MODIFIED : TYPE.UNCHANGED, left, right, { children: items, ...container(left, isArr) })
47-
}
51+
: allKeys(left, right).map(k => diff(left?.[k], right?.[k], k));
52+
const hasDiff = items.some(c => c.hasDiff);
53+
return createNode(key, hasDiff ? TYPE.MODIFIED : TYPE.UNCHANGED, left, right, {
54+
children: items,
55+
...getContainerProps(left)
56+
});
57+
};
4858

4959
const diff = (left, right, key = 'root') => {
5060
if (left === undefined) {
51-
const extra = isObj(right) ? { children: mapChildren(right, 'added'), ...container(right, Array.isArray(right)) } : {};
52-
return node(key, TYPE.ADDED, left, right, extra);
61+
const extra = isObj(right) ? {
62+
children: mapChildrenForSide(right, 'added'),
63+
...getContainerProps(right)
64+
} : {};
65+
return createNode(key, TYPE.ADDED, left, right, extra);
5366
}
5467
if (right === undefined) {
55-
const extra = isObj(left) ? { children: mapChildren(left, 'removed'), ...container(left, Array.isArray(left)) } : {};
56-
return node(key, TYPE.REMOVED, left, right, extra);
68+
const extra = isObj(left) ? {
69+
children: mapChildrenForSide(left, 'removed'),
70+
...getContainerProps(left)
71+
} : {};
72+
return createNode(key, TYPE.REMOVED, left, right, extra);
5773
}
5874
if (!isObj(left) && !isObj(right)) {
59-
if (left === right) return node(key, TYPE.UNCHANGED, left, right);
60-
return node(key, TYPE.MODIFIED, left, right);
75+
return createNode(key, left === right ? TYPE.UNCHANGED : TYPE.MODIFIED, left, right);
6176
}
6277
if (typeOf(left) !== typeOf(right)) {
63-
return node(key, TYPE.MODIFIED, left, right, { children: [], ...container(left, Array.isArray(left)) });
78+
return createNode(key, TYPE.MODIFIED, left, right, {
79+
children: [],
80+
...getContainerProps(left)
81+
});
6482
}
6583
return diffContainer(left, right, key, Array.isArray(left));
6684
};
6785

68-
export { diff, TYPE }
86+
export { diff, TYPE };

src/lib/styles.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default `
44
--bg: #18181b; --bg2: #27272a; --bdr: #3f3f46;
55
--txt: #fafafa; --dim: #a1a1aa;
66
--key: #38bdf8; --str: #a78bfa; --num: #34d399; --bool: #fb923c; --nul: #f472b6; --br: #71717a;
7+
--slider: var(--mod);
78
display: flex; flex-direction: column; font: 13px 'JetBrains Mono', 'Fira Code', monospace;
89
background: var(--bg); color: var(--txt); border-radius: 12px; overflow: hidden;
910
}
@@ -43,12 +44,17 @@ export default `
4344
.preview::after { content: ' items'; }
4445
.stats { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 1rem; padding: .75rem 1rem; background: var(--bg); border-bottom: 2px solid var(--bdr); font-size: 12px; }
4546
.stats-items { display: grid; grid-auto-flow: column; gap: 2rem; justify-content: start; }
46-
.stats-buttons { display: flex; gap: 0.5rem; }
47-
.btn-filter, .btn-collapse, .btn-expand { padding: 0.5rem; background: var(--bg2); border: 1px solid var(--bdr); border-radius: 10px; color: var(--txt); cursor: pointer; transition: background .15s, border-color .15s; display: flex; align-items: center; justify-content: center; }
48-
.btn-filter svg, .btn-collapse svg, .btn-expand svg { width: 18px; height: 18px; }
49-
.btn-filter:hover, .btn-collapse:hover, .btn-expand:hover { background: rgba(0,0,0,.05); box-shadow: 0 0 4px var(--bdr); }
50-
.btn-filter .checkbox-icon { opacity: 0.3; transition: opacity .15s; }
51-
.btn-filter .checkbox-icon.checked { opacity: 1; }
47+
.stats-buttons { display: flex; gap: 0.5rem; align-items: center; }
48+
.switch { display: inline-block; cursor: pointer; }
49+
.checkbox { display: none; }
50+
.slider { width: 48px; height: 24px; background-color: var(--bdr); border-radius: 16px; overflow: hidden; display: flex; align-items: center; border: 3px solid transparent; transition: .3s; box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.25) inset; cursor: pointer; }
51+
.slider::before { content: ''; display: block; width: 100%; height: 100%; background-color: var(--txt); transform: translateX(-24px); border-radius: 16px; transition: .3s; box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.25); }
52+
.checkbox:checked ~ .slider::before { transform: translateX(24px); box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.25); }
53+
.checkbox:checked ~ .slider { background-color: var(--slider); }
54+
.checkbox:active ~ .slider::before { transform: translate(0); }
55+
.btn-collapse, .btn-expand { padding: 0.5rem; background: var(--bg2); border: 1px solid var(--bdr); border-radius: 10px; color: var(--txt); cursor: pointer; transition: background .15s, border-color .15s; display: flex; align-items: center; justify-content: center; }
56+
.btn-collapse svg, .btn-expand svg { width: 18px; height: 18px; }
57+
.btn-collapse:hover, .btn-expand:hover { background: rgba(0,0,0,.05); box-shadow: 0 0 4px var(--bdr); }
5258
.stat { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: .35rem; }
5359
.stat .dot { width: 8px; height: 8px; }
5460
.stat-added .dot { background: var(--add); }

0 commit comments

Comments
 (0)