@inkra/react 0.10.0 → 0.12.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.
- package/README.md +62 -3
- package/dist/dependency-graph/index.d.ts +47 -0
- package/dist/dependency-graph/index.js +177 -0
- package/dist/dependency-graph/model.d.ts +68 -0
- package/dist/dependency-graph/model.js +198 -0
- package/dist/i18n/catalog.d.ts +1 -1
- package/dist/i18n/catalog.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/log/index.js +7 -6
- package/dist/node-graph/index.d.ts +15 -2
- package/dist/node-graph/index.js +17 -8
- package/dist/number-ticker/MorphValue.d.ts +7 -0
- package/dist/number-ticker/MorphValue.js +10 -0
- package/dist/number-ticker/index.d.ts +1 -1
- package/dist/number-ticker/index.js +65 -11
- package/dist/number-ticker/morph-motion.d.ts +46 -0
- package/dist/number-ticker/morph-motion.js +121 -0
- package/dist/number-ticker/morph.d.ts +7 -0
- package/dist/number-ticker/morph.js +78 -0
- package/dist/styles/dependency-graph.css +176 -0
- package/dist/styles/log.css +5 -18
- package/dist/styles/number-ticker.css +41 -0
- package/dist/styles.css +80 -18
- package/package.json +8 -2
|
@@ -7,6 +7,9 @@ import { canAnimateTickerValue, createNumberFormatter, createTickerFormatter, ea
|
|
|
7
7
|
import { ExactDecimal } from './precision.js';
|
|
8
8
|
import { RollingValue } from './RollingValue.js';
|
|
9
9
|
import { createRollingMotion, rollingTokens, sameRollingTokens } from './rolling.js';
|
|
10
|
+
import { MorphValue } from './MorphValue.js';
|
|
11
|
+
import { morphTokenMapping } from './morph.js';
|
|
12
|
+
import { createMorphMotion, measureMorphWidths } from './morph-motion.js';
|
|
10
13
|
function usePrefersReducedMotion() {
|
|
11
14
|
const [reduced, setReduced] = useState(false);
|
|
12
15
|
useBrowserLayoutEffect(() => {
|
|
@@ -41,6 +44,8 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
41
44
|
const [roll, setRoll] = useState(null);
|
|
42
45
|
const [rollProgress, setRollProgress] = useState(1);
|
|
43
46
|
const rollFrameRef = useRef(null);
|
|
47
|
+
const [morph, setMorph] = useState(null);
|
|
48
|
+
const morphFrameRef = useRef(null);
|
|
44
49
|
const reducedMotion = usePrefersReducedMotion();
|
|
45
50
|
const localeKey = Array.isArray(locale) ? locale.join('\u0000') : locale;
|
|
46
51
|
const formatOptionsKey = JSON.stringify(formatOptions ?? {});
|
|
@@ -51,12 +56,18 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
51
56
|
const announcementFormatter = useMemo(() => createNumberFormatter(locale, compact ? { ...formatOptions, notation: 'standard' } : formatOptions), [localeKey, formatOptionsKey, compact]);
|
|
52
57
|
useImperativeHandle(forwardedRef, () => rootRef.current);
|
|
53
58
|
const rollRequested = animation === 'roll' && !renderValue;
|
|
54
|
-
const
|
|
59
|
+
const morphRequested = animation === 'morph' && !renderValue;
|
|
60
|
+
const finalParts = useMemo(() => rollRequested || morphRequested ? formatter.formatToParts(value) : [], [formatter, value, rollRequested, morphRequested]);
|
|
61
|
+
const finalTokens = useMemo(() => rollingTokens(finalParts), [finalParts]);
|
|
55
62
|
// Keep exact large values readable without creating thousands of painted reels.
|
|
56
63
|
const useRoll = rollRequested && finalTokens.length <= 128;
|
|
64
|
+
const useMorph = morphRequested && finalTokens.length <= 128;
|
|
65
|
+
const useTokens = useRoll || useMorph;
|
|
57
66
|
const settledTokensRef = useRef(finalTokens);
|
|
67
|
+
const morphPartsRef = useRef(finalParts);
|
|
58
68
|
const previousFormatterRef = useRef(formatter);
|
|
59
69
|
const previousUseRollRef = useRef(useRoll);
|
|
70
|
+
const previousAnimationRef = useRef(animation);
|
|
60
71
|
const columns = useMemo(() => roll?.columns ?? createRollingMotion(finalTokens, finalTokens, 'up'), [roll, finalTokens]);
|
|
61
72
|
useBrowserLayoutEffect(() => {
|
|
62
73
|
const previousTarget = targetRef.current;
|
|
@@ -87,12 +98,13 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
87
98
|
const from = displayedRef.current;
|
|
88
99
|
const formatChanged = previousFormatterRef.current !== formatter;
|
|
89
100
|
previousFormatterRef.current = formatter;
|
|
90
|
-
const modeChanged = previousUseRollRef.current !== useRoll;
|
|
101
|
+
const modeChanged = previousUseRollRef.current !== useRoll || previousAnimationRef.current !== animation;
|
|
91
102
|
previousUseRollRef.current = useRoll;
|
|
103
|
+
previousAnimationRef.current = animation;
|
|
92
104
|
if (!animate || reduceImmediately || formatChanged || modeChanged
|
|
93
|
-
|| (
|
|
94
|
-
|| (rollRequested && (!
|
|
95
|
-
|| !canAnimateTickerValue(from, value, duration)) {
|
|
105
|
+
|| (useTokens && !rollFrameRef.current && (!morphFrameRef.current || morphFrameRef.current.progress >= 1) && sameRollingTokens(settledTokensRef.current, finalTokens))
|
|
106
|
+
|| ((rollRequested || morphRequested) && (!useTokens || settledTokensRef.current.length > 128))
|
|
107
|
+
|| (!canAnimateTickerValue(from, value, duration) && !(useMorph && canAnimateTickerValue(previousTarget, value, duration)))) {
|
|
96
108
|
displayedRef.current = value;
|
|
97
109
|
setDisplayedValue(value);
|
|
98
110
|
setAnnouncedValue(value);
|
|
@@ -101,9 +113,44 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
101
113
|
setRollProgress(1);
|
|
102
114
|
settledTokensRef.current = finalTokens;
|
|
103
115
|
rollFrameRef.current = null;
|
|
116
|
+
setMorph(null);
|
|
117
|
+
morphFrameRef.current = null;
|
|
118
|
+
morphPartsRef.current = finalParts;
|
|
104
119
|
return undefined;
|
|
105
120
|
}
|
|
106
|
-
if (
|
|
121
|
+
if (useMorph) {
|
|
122
|
+
const direction = rollDirection === 'auto'
|
|
123
|
+
? (new ExactDecimal(String(value)).gt(String(previousTarget)) ? 'up' : 'down') : rollDirection;
|
|
124
|
+
const previousTokens = morphFrameRef.current?.motion.tokens ?? settledTokensRef.current;
|
|
125
|
+
const zeroPart = formatter.formatToParts(0).find(part => part.type === 'integer')?.value ?? '0';
|
|
126
|
+
const zero = Array.from(zeroPart).slice(-1)[0] ?? '0';
|
|
127
|
+
const mapping = morphTokenMapping(morphPartsRef.current, finalParts, previousTarget, value, zero);
|
|
128
|
+
const motion = createMorphMotion(previousTokens, finalTokens, mapping, direction, measureMorphWidths(rootRef.current, [...previousTokens, ...finalTokens]), morphFrameRef.current ?? undefined);
|
|
129
|
+
// Bound interrupted layers as well as formatted target token counts.
|
|
130
|
+
if (motion.glyphs.length > 512) {
|
|
131
|
+
displayedRef.current = value;
|
|
132
|
+
setDisplayedValue(value);
|
|
133
|
+
setAnnouncedValue(value);
|
|
134
|
+
setAnimating(false);
|
|
135
|
+
setMorph(null);
|
|
136
|
+
morphFrameRef.current = null;
|
|
137
|
+
morphPartsRef.current = finalParts;
|
|
138
|
+
settledTokensRef.current = finalTokens;
|
|
139
|
+
setRoll(null);
|
|
140
|
+
rollFrameRef.current = null;
|
|
141
|
+
setRollProgress(1);
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
setMorph(motion);
|
|
145
|
+
morphFrameRef.current = { motion, progress: 0 };
|
|
146
|
+
morphPartsRef.current = finalParts;
|
|
147
|
+
setRoll(null);
|
|
148
|
+
rollFrameRef.current = null;
|
|
149
|
+
setRollProgress(0);
|
|
150
|
+
}
|
|
151
|
+
else if (useRoll) {
|
|
152
|
+
setMorph(null);
|
|
153
|
+
morphFrameRef.current = null;
|
|
107
154
|
const direction = rollDirection === 'auto'
|
|
108
155
|
? (new ExactDecimal(String(value)).gt(String(from)) ? 'up' : 'down') : rollDirection;
|
|
109
156
|
const nextColumns = createRollingMotion(settledTokensRef.current, finalTokens, direction, rollFrameRef.current ?? undefined);
|
|
@@ -114,6 +161,8 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
114
161
|
else {
|
|
115
162
|
setRoll(null);
|
|
116
163
|
rollFrameRef.current = null;
|
|
164
|
+
setMorph(null);
|
|
165
|
+
morphFrameRef.current = null;
|
|
117
166
|
}
|
|
118
167
|
let startTime = null;
|
|
119
168
|
setAnimating(true);
|
|
@@ -125,10 +174,12 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
125
174
|
const bounded = Number.isFinite(eased) ? Math.min(1, Math.max(0, eased)) : progress;
|
|
126
175
|
const next = interpolateTickerValue(from, value, bounded);
|
|
127
176
|
displayedRef.current = next;
|
|
128
|
-
if (
|
|
177
|
+
if (useTokens) {
|
|
129
178
|
setRollProgress(bounded);
|
|
130
179
|
if (rollFrameRef.current)
|
|
131
180
|
rollFrameRef.current.progress = bounded;
|
|
181
|
+
if (morphFrameRef.current)
|
|
182
|
+
morphFrameRef.current.progress = bounded;
|
|
132
183
|
}
|
|
133
184
|
else
|
|
134
185
|
setDisplayedValue(next);
|
|
@@ -145,6 +196,8 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
145
196
|
setRollProgress(1);
|
|
146
197
|
settledTokensRef.current = finalTokens;
|
|
147
198
|
rollFrameRef.current = null;
|
|
199
|
+
if (morphFrameRef.current)
|
|
200
|
+
morphFrameRef.current.progress = 1;
|
|
148
201
|
}
|
|
149
202
|
};
|
|
150
203
|
frameRef.current = requestAnimationFrame(tick);
|
|
@@ -154,7 +207,7 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
154
207
|
frameRef.current = null;
|
|
155
208
|
}
|
|
156
209
|
};
|
|
157
|
-
}, [animate, deltaDuration, duration, easing, reducedMotion, showDelta, value, useRoll, rollRequested, rollDirection, formatter]);
|
|
210
|
+
}, [animate, deltaDuration, duration, easing, reducedMotion, showDelta, value, useRoll, useMorph, useTokens, rollRequested, morphRequested, rollDirection, formatter, animation]);
|
|
158
211
|
useEffect(() => () => {
|
|
159
212
|
if (frameRef.current !== null)
|
|
160
213
|
cancelAnimationFrame(frameRef.current);
|
|
@@ -168,9 +221,10 @@ export const NumberTicker = forwardRef(function NumberTicker({ animate = true, a
|
|
|
168
221
|
.filter(Boolean).join(' ');
|
|
169
222
|
const accessibleLabel = getValueLabel?.(announcedValue, announcedFormattedValue) ?? defaultAccessibleLabel;
|
|
170
223
|
const valueContent = renderValue?.(displayedValue, { animating, formattedValue })
|
|
171
|
-
?? (
|
|
172
|
-
? _jsx(
|
|
173
|
-
|
|
224
|
+
?? (useMorph ? _jsx(MorphValue, { tokens: finalTokens, motion: morph, progress: morph ? rollProgress : 1 })
|
|
225
|
+
: useRoll ? _jsx(RollingValue, { columns: columns, progress: roll ? rollProgress : 1, direction: roll?.direction ?? 'up' }) : animating
|
|
226
|
+
? _jsx("span", { className: "inkra-number-ticker__native-value", children: formattedValue })
|
|
227
|
+
: _jsx(PencilText, { seed: `number-ticker-${finalFormattedValue}`, children: finalFormattedValue }));
|
|
174
228
|
const deltaContent = delta === null ? null : (() => {
|
|
175
229
|
const formattedDelta = formatDerivedTickerDelta(deltaFormatter, delta);
|
|
176
230
|
const details = {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { RollingToken } from './rolling.js';
|
|
2
|
+
export interface MorphGlyph {
|
|
3
|
+
id: string;
|
|
4
|
+
referenceKey: string;
|
|
5
|
+
text: string;
|
|
6
|
+
digit: boolean;
|
|
7
|
+
width: number;
|
|
8
|
+
target: boolean;
|
|
9
|
+
fromX: number;
|
|
10
|
+
toX: number;
|
|
11
|
+
fromY: number;
|
|
12
|
+
toY: number;
|
|
13
|
+
fromOpacity: number;
|
|
14
|
+
toOpacity: number;
|
|
15
|
+
}
|
|
16
|
+
export interface MorphMotion {
|
|
17
|
+
glyphs: MorphGlyph[];
|
|
18
|
+
tokens: RollingToken[];
|
|
19
|
+
width: number;
|
|
20
|
+
targetWidth: number;
|
|
21
|
+
kind: 'shift' | 'roll';
|
|
22
|
+
}
|
|
23
|
+
export interface MorphFrame {
|
|
24
|
+
motion: MorphMotion;
|
|
25
|
+
progress: number;
|
|
26
|
+
}
|
|
27
|
+
export type MorphWidths = (token: RollingToken) => number;
|
|
28
|
+
export declare function measureMorphWidths(root: HTMLElement | null, tokens: RollingToken[]): MorphWidths;
|
|
29
|
+
export declare function sampleMorphGlyph(glyph: MorphGlyph, progress: number): {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
opacity: number;
|
|
33
|
+
id: string;
|
|
34
|
+
referenceKey: string;
|
|
35
|
+
text: string;
|
|
36
|
+
digit: boolean;
|
|
37
|
+
width: number;
|
|
38
|
+
target: boolean;
|
|
39
|
+
fromX: number;
|
|
40
|
+
toX: number;
|
|
41
|
+
fromY: number;
|
|
42
|
+
toY: number;
|
|
43
|
+
fromOpacity: number;
|
|
44
|
+
toOpacity: number;
|
|
45
|
+
};
|
|
46
|
+
export declare function createMorphMotion(from: RollingToken[], to: RollingToken[], mapping: Array<[number, number]> | null, direction: 'up' | 'down', widths: MorphWidths, previous?: MorphFrame): MorphMotion;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { rollMix } from './rolling.js';
|
|
2
|
+
let nextGlyph = 0;
|
|
3
|
+
export function measureMorphWidths(root, tokens) {
|
|
4
|
+
if (!root || typeof document === 'undefined')
|
|
5
|
+
return token => token.digit ? 1 : Math.max(.25, Array.from(token.text).length);
|
|
6
|
+
const style = getComputedStyle(root);
|
|
7
|
+
const probe = document.createElement('span');
|
|
8
|
+
Object.assign(probe.style, { position: 'fixed', visibility: 'hidden', pointerEvents: 'none',
|
|
9
|
+
font: style.font, fontVariantNumeric: 'tabular-nums lining-nums', fontKerning: 'none',
|
|
10
|
+
fontVariantLigatures: 'none', letterSpacing: '0', whiteSpace: 'pre', width: '1ch', display: 'inline-block' });
|
|
11
|
+
document.body.append(probe);
|
|
12
|
+
const ch = probe.getBoundingClientRect().width || 1;
|
|
13
|
+
probe.style.width = 'auto';
|
|
14
|
+
const widths = new Map();
|
|
15
|
+
// Measurement happens once when a target changes, never in the frame loop.
|
|
16
|
+
const measure = (token) => {
|
|
17
|
+
if (token.digit)
|
|
18
|
+
return 1;
|
|
19
|
+
if (!widths.has(token.text)) {
|
|
20
|
+
probe.textContent = token.text;
|
|
21
|
+
widths.set(token.text, Math.max(.05, probe.getBoundingClientRect().width / ch));
|
|
22
|
+
}
|
|
23
|
+
return widths.get(token.text);
|
|
24
|
+
};
|
|
25
|
+
// The returned callback cannot retain a live measuring node.
|
|
26
|
+
tokens.forEach(measure);
|
|
27
|
+
const result = token => token.digit ? 1 : widths.get(token.text) ?? Math.max(.25, Array.from(token.text).length);
|
|
28
|
+
probe.remove();
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function layout(tokens, widths) {
|
|
32
|
+
const total = tokens.reduce((sum, token) => sum + widths(token), 0);
|
|
33
|
+
let x = -total;
|
|
34
|
+
return { width: total, items: tokens.map(token => {
|
|
35
|
+
const width = widths(token);
|
|
36
|
+
const item = { ...token, x, width };
|
|
37
|
+
x += width;
|
|
38
|
+
return item;
|
|
39
|
+
}) };
|
|
40
|
+
}
|
|
41
|
+
export function sampleMorphGlyph(glyph, progress) {
|
|
42
|
+
const fade = progress * progress * (3 - 2 * progress);
|
|
43
|
+
return { ...glyph, x: rollMix(glyph.fromX, glyph.toX, progress), y: rollMix(glyph.fromY, glyph.toY, progress),
|
|
44
|
+
opacity: rollMix(glyph.fromOpacity, glyph.toOpacity, fade) };
|
|
45
|
+
}
|
|
46
|
+
export function createMorphMotion(from, to, mapping, direction, widths, previous) {
|
|
47
|
+
const before = layout(from, widths), after = layout(to, widths);
|
|
48
|
+
const source = previous ? previous.motion.glyphs.map(glyph => sampleMorphGlyph(glyph, previous.progress))
|
|
49
|
+
.filter(glyph => glyph.opacity > .001 && Math.abs(glyph.y) < 1.001)
|
|
50
|
+
: before.items.map(token => ({ id: `settled-${token.key}`, referenceKey: token.key, text: token.text,
|
|
51
|
+
digit: token.digit, width: token.width, target: true, x: token.x, y: 0, opacity: 1 }));
|
|
52
|
+
const used = new Set();
|
|
53
|
+
const glyphs = [];
|
|
54
|
+
const sign = direction === 'up' ? -1 : 1;
|
|
55
|
+
const pairs = mapping ?? from.flatMap((token, index) => {
|
|
56
|
+
const next = to.findIndex(item => item.key === token.key);
|
|
57
|
+
return next < 0 ? [] : [[index, next]];
|
|
58
|
+
});
|
|
59
|
+
const oldIndexes = new Map(pairs.map(([left, right]) => [right, left]));
|
|
60
|
+
const findSource = (index) => source.filter(item => item.referenceKey === from[index]?.key)
|
|
61
|
+
.sort((left, right) => right.opacity - left.opacity);
|
|
62
|
+
const decimalEnd = (items) => {
|
|
63
|
+
const integers = items.filter(item => item.key.startsWith('integer-'));
|
|
64
|
+
const integer = integers[integers.length - 1];
|
|
65
|
+
return integer ? integer.x + integer.width : 0;
|
|
66
|
+
};
|
|
67
|
+
for (let index = 0; index < to.length; index++) {
|
|
68
|
+
const token = to[index], target = after.items[index];
|
|
69
|
+
const oldIndex = oldIndexes.get(index);
|
|
70
|
+
const candidates = oldIndex === undefined ? [] : findSource(oldIndex);
|
|
71
|
+
const existing = candidates.find(item => item.text === token.text && !used.has(item.id));
|
|
72
|
+
if (existing) {
|
|
73
|
+
used.add(existing.id);
|
|
74
|
+
glyphs.push({ id: existing.id, referenceKey: token.key, text: token.text, digit: token.digit,
|
|
75
|
+
width: existing.width, target: true, fromX: existing.x, toX: target.x,
|
|
76
|
+
fromY: existing.y, toY: 0, fromOpacity: existing.opacity, toOpacity: 1 });
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
let x = candidates[0]?.x ?? before.items[oldIndex ?? -1]?.x ?? target.x;
|
|
80
|
+
let y = token.digit && !mapping ? -sign : 0;
|
|
81
|
+
if (mapping) {
|
|
82
|
+
if (token.key.startsWith('decimal-'))
|
|
83
|
+
x = decimalEnd(before.items);
|
|
84
|
+
else if (token.digit) {
|
|
85
|
+
const anchor = pairs.filter(([left, right]) => right < index && from[left].digit)
|
|
86
|
+
.sort((left, right) => right[1] - left[1])[0];
|
|
87
|
+
if (anchor) {
|
|
88
|
+
const visible = findSource(anchor[0])[0];
|
|
89
|
+
x = (visible?.x ?? before.items[anchor[0]].x) + target.x - after.items[anchor[1]].x;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
glyphs.push({ id: `morph-${nextGlyph++}`, referenceKey: token.key, text: token.text,
|
|
94
|
+
digit: token.digit, width: target.width, target: true, fromX: x, toX: target.x,
|
|
95
|
+
fromY: y, toY: 0, fromOpacity: mapping || !token.digit ? 0 : 1, toOpacity: 1 });
|
|
96
|
+
}
|
|
97
|
+
for (const item of source) {
|
|
98
|
+
if (used.has(item.id))
|
|
99
|
+
continue;
|
|
100
|
+
let x = item.x;
|
|
101
|
+
let y = item.digit && !mapping ? sign : 0;
|
|
102
|
+
if (mapping) {
|
|
103
|
+
if (item.referenceKey.startsWith('decimal-'))
|
|
104
|
+
x = decimalEnd(after.items);
|
|
105
|
+
else {
|
|
106
|
+
const oldIndex = from.findIndex(token => token.key === item.referenceKey);
|
|
107
|
+
const anchor = pairs.filter(([left]) => left < oldIndex && from[left].digit)
|
|
108
|
+
.sort((left, right) => right[0] - left[0])[0];
|
|
109
|
+
if (anchor) {
|
|
110
|
+
const visible = findSource(anchor[0])[0];
|
|
111
|
+
x += after.items[anchor[1]].x - (visible?.x ?? before.items[anchor[0]].x);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
glyphs.push({ id: item.id, referenceKey: item.referenceKey, text: item.text, digit: item.digit,
|
|
116
|
+
width: item.width, target: false, fromX: item.x, toX: x, fromY: item.y, toY: y,
|
|
117
|
+
fromOpacity: item.opacity, toOpacity: mapping || !item.digit ? 0 : 1 });
|
|
118
|
+
}
|
|
119
|
+
return { glyphs, tokens: to, width: Math.max(before.width, after.width, previous && previous.progress < 1 ? previous.motion.width : 0),
|
|
120
|
+
targetWidth: after.width, kind: mapping ? 'shift' : 'roll' };
|
|
121
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NumberTickerValue } from './precision.js';
|
|
2
|
+
/**
|
|
3
|
+
* Match an exact decimal shift, rather than guessing identity from equal glyphs.
|
|
4
|
+
* Pair indexes address rollingTokens(parts), including unchanged typed symbols.
|
|
5
|
+
* A null result leaves the transition to the existing place-aligned reels.
|
|
6
|
+
*/
|
|
7
|
+
export declare function morphTokenMapping(fromParts: Intl.NumberFormatPart[], toParts: Intl.NumberFormatPart[], fromValue: NumberTickerValue, toValue: NumberTickerValue, zeroGlyph?: string): Array<[number, number]> | null;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ExactDecimal, tickerDecimal } from './precision.js';
|
|
2
|
+
import { rollingTokens } from './rolling.js';
|
|
3
|
+
const numericParts = new Set(['integer', 'fraction', 'decimal', 'group']);
|
|
4
|
+
const exponentParts = new Set(['exponentSeparator', 'exponentMinusSign', 'exponentInteger']);
|
|
5
|
+
function displayDigits(parts, zeroGlyph) {
|
|
6
|
+
const tokens = rollingTokens(parts);
|
|
7
|
+
const indexes = tokens.flatMap((token, index) => token.digit ? [index] : []);
|
|
8
|
+
const text = indexes.map(index => tokens[index].text);
|
|
9
|
+
const first = text.findIndex(digit => digit !== zeroGlyph);
|
|
10
|
+
if (first < 0)
|
|
11
|
+
return null;
|
|
12
|
+
let last = text.length - 1;
|
|
13
|
+
while (text[last] === zeroGlyph)
|
|
14
|
+
last--;
|
|
15
|
+
const integerLength = parts.filter(part => part.type === 'integer')
|
|
16
|
+
.reduce((length, part) => length + Array.from(part.value).length, 0);
|
|
17
|
+
return { indexes, text, first, last, exponent: integerLength - first - 1 };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Match an exact decimal shift, rather than guessing identity from equal glyphs.
|
|
21
|
+
* Pair indexes address rollingTokens(parts), including unchanged typed symbols.
|
|
22
|
+
* A null result leaves the transition to the existing place-aligned reels.
|
|
23
|
+
*/
|
|
24
|
+
export function morphTokenMapping(fromParts, toParts, fromValue, toValue, zeroGlyph = '0') {
|
|
25
|
+
const from = tickerDecimal(fromValue);
|
|
26
|
+
const to = tickerDecimal(toValue);
|
|
27
|
+
if (!from.isFinite() || !to.isFinite() || from.isZero() || to.isZero()
|
|
28
|
+
|| from.isNegative() !== to.isNegative())
|
|
29
|
+
return null;
|
|
30
|
+
const shift = to.e - from.e;
|
|
31
|
+
if (shift === 0 || !from.times(new ExactDecimal(10).pow(shift)).eq(to))
|
|
32
|
+
return null;
|
|
33
|
+
// Unit/sign/literal changes are separate semantic transitions. Exponents have
|
|
34
|
+
// their own digit domain and must never be mistaken for mantissa digits.
|
|
35
|
+
if ([...fromParts, ...toParts].some(part => exponentParts.has(part.type)))
|
|
36
|
+
return null;
|
|
37
|
+
const symbols = (parts) => parts.filter(part => !numericParts.has(part.type));
|
|
38
|
+
const fromSymbols = symbols(fromParts);
|
|
39
|
+
const toSymbols = symbols(toParts);
|
|
40
|
+
if (fromSymbols.length !== toSymbols.length || fromSymbols.some((part, index) => {
|
|
41
|
+
const next = toSymbols[index];
|
|
42
|
+
return part.type !== next.type || part.value !== next.value;
|
|
43
|
+
}))
|
|
44
|
+
return null;
|
|
45
|
+
const before = displayDigits(fromParts, zeroGlyph);
|
|
46
|
+
const after = displayDigits(toParts, zeroGlyph);
|
|
47
|
+
if (!before || !after || after.exponent - before.exponent !== shift)
|
|
48
|
+
return null;
|
|
49
|
+
const core = before.text.slice(before.first, before.last + 1);
|
|
50
|
+
const nextCore = after.text.slice(after.first, after.last + 1);
|
|
51
|
+
// Rounding may change the visible significand even for an exact raw shift.
|
|
52
|
+
if (core.length !== nextCore.length || core.some((digit, index) => digit !== nextCore[index]))
|
|
53
|
+
return null;
|
|
54
|
+
const mapping = [];
|
|
55
|
+
for (let index = 0; index < core.length; index++) {
|
|
56
|
+
mapping.push([before.indexes[before.first + index], after.indexes[after.first + index]]);
|
|
57
|
+
}
|
|
58
|
+
// Edge zeros retain their ordinal distance from the significant core. This
|
|
59
|
+
// preserves 10 as the first 10 in 1000 without matching arbitrary repeats.
|
|
60
|
+
for (let offset = 1; offset <= Math.min(before.first, after.first); offset++) {
|
|
61
|
+
mapping.push([before.indexes[before.first - offset], after.indexes[after.first - offset]]);
|
|
62
|
+
}
|
|
63
|
+
const trailing = Math.min(before.text.length - before.last - 1, after.text.length - after.last - 1);
|
|
64
|
+
for (let offset = 1; offset <= trailing; offset++) {
|
|
65
|
+
mapping.push([before.indexes[before.last + offset], after.indexes[after.last + offset]]);
|
|
66
|
+
}
|
|
67
|
+
const fromTokens = rollingTokens(fromParts);
|
|
68
|
+
const toTokens = rollingTokens(toParts);
|
|
69
|
+
const targetSymbols = new Map(toTokens.flatMap((token, index) => token.digit ? [] : [[token.key, { token, index }]]));
|
|
70
|
+
fromTokens.forEach((token, index) => {
|
|
71
|
+
if (token.digit)
|
|
72
|
+
return;
|
|
73
|
+
const next = targetSymbols.get(token.key);
|
|
74
|
+
if (next && next.token.text === token.text)
|
|
75
|
+
mapping.push([index, next.index]);
|
|
76
|
+
});
|
|
77
|
+
return mapping.sort((left, right) => left[0] - right[0]);
|
|
78
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
.inkra-node-graph {
|
|
2
|
+
position: relative;
|
|
3
|
+
isolation: isolate;
|
|
4
|
+
inline-size: 100%;
|
|
5
|
+
min-inline-size: 0;
|
|
6
|
+
block-size: min(68vh, 620px);
|
|
7
|
+
min-block-size: 320px;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
color: var(--inkra-ink);
|
|
10
|
+
background: color-mix(in srgb, var(--inkra-paper) 92%, transparent);
|
|
11
|
+
border-radius: var(--inkra-radius-lg);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.inkra-node-graph .react-flow {
|
|
15
|
+
position: relative;
|
|
16
|
+
z-index: 0;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
direction: inherit;
|
|
19
|
+
inline-size: 100%;
|
|
20
|
+
block-size: 100%;
|
|
21
|
+
background: transparent;
|
|
22
|
+
--xy-edge-stroke: var(--inkra-ink-muted);
|
|
23
|
+
--xy-edge-stroke-selected: var(--inkra-accent-strong);
|
|
24
|
+
--xy-connectionline-stroke: var(--inkra-accent-strong);
|
|
25
|
+
--xy-selection-background-color: color-mix(in srgb, var(--inkra-selection-pigment) 13%, transparent);
|
|
26
|
+
--xy-selection-border: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.inkra-node-graph .react-flow__container { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; }
|
|
30
|
+
.inkra-node-graph .react-flow__pane { z-index: 1; touch-action: none; }
|
|
31
|
+
.inkra-node-graph .react-flow__pane.draggable { cursor: grab; }
|
|
32
|
+
.inkra-node-graph .react-flow__pane.dragging { cursor: grabbing; }
|
|
33
|
+
.inkra-node-graph .react-flow__pane.selection { cursor: pointer; }
|
|
34
|
+
.inkra-node-graph .react-flow__viewport { position: absolute; inset: 0; z-index: 2; transform-origin: 0 0; pointer-events: none; }
|
|
35
|
+
.inkra-node-graph .react-flow__renderer { z-index: 4; }
|
|
36
|
+
.inkra-node-graph .react-flow__edges { position: absolute; }
|
|
37
|
+
.inkra-node-graph .react-flow__edges svg { position: absolute; overflow: visible; pointer-events: none; }
|
|
38
|
+
.inkra-node-graph .react-flow__edge { pointer-events: visibleStroke; }
|
|
39
|
+
.inkra-node-graph .react-flow__edge.selectable { cursor: pointer; }
|
|
40
|
+
.inkra-node-graph .react-flow__edge.inactive { pointer-events: none; }
|
|
41
|
+
.inkra-node-graph .react-flow__edge:focus,
|
|
42
|
+
.inkra-node-graph .react-flow__edge:focus-visible { outline: none; }
|
|
43
|
+
.inkra-node-graph svg.react-flow__connectionline { position: absolute; z-index: 8; overflow: visible; pointer-events: none; }
|
|
44
|
+
.inkra-node-graph .react-flow__nodes { pointer-events: none; transform-origin: 0 0; }
|
|
45
|
+
.inkra-node-graph .react-flow__node { position: absolute; box-sizing: border-box; transform-origin: 0 0; pointer-events: all; user-select: none; }
|
|
46
|
+
.inkra-node-graph .react-flow__node.selectable { cursor: pointer; }
|
|
47
|
+
.inkra-node-graph .react-flow__node.draggable { cursor: grab; }
|
|
48
|
+
.inkra-node-graph .react-flow__node.draggable.dragging { cursor: grabbing; }
|
|
49
|
+
.inkra-node-graph .react-flow__node:focus { outline: none; }
|
|
50
|
+
.inkra-node-graph .react-flow__nodesselection { z-index: 3; transform-origin: left top; pointer-events: none; }
|
|
51
|
+
.inkra-node-graph .react-flow__nodesselection-rect { position: absolute; pointer-events: all; cursor: grab; }
|
|
52
|
+
.inkra-node-graph .react-flow__nodesselection-rect:focus,
|
|
53
|
+
.inkra-node-graph .react-flow__nodesselection-rect:focus-visible { outline: none; }
|
|
54
|
+
.inkra-node-graph .react-flow__selection { z-index: 6; }
|
|
55
|
+
.inkra-node-graph .react-flow__panel { position: absolute; z-index: 7; margin: var(--inkra-space-3); }
|
|
56
|
+
.inkra-node-graph .react-flow__panel.top { inset-block-start: 0; }
|
|
57
|
+
.inkra-node-graph .react-flow__panel.bottom { inset-block-end: 0; }
|
|
58
|
+
.inkra-node-graph .react-flow__panel.left { inset-inline-start: 0; }
|
|
59
|
+
.inkra-node-graph .react-flow__panel.right { inset-inline-end: 0; }
|
|
60
|
+
.inkra-node-graph .react-flow__pane.selection .react-flow__panel { pointer-events: none; }
|
|
61
|
+
.inkra-node-graph .react-flow__selection-rect,
|
|
62
|
+
.inkra-node-graph .react-flow__nodesselection-rect { border: 0; background: color-mix(in srgb, var(--inkra-selection-pigment) 13%, transparent); }
|
|
63
|
+
.inkra-node-graph .react-flow__viewport-portal { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; pointer-events: none; user-select: none; }
|
|
64
|
+
.inkra-node-graph .react-flow__attribution { color: var(--inkra-ink-muted); font-size: 10px; line-height: 1.2; }
|
|
65
|
+
.inkra-node-graph .react-flow__attribution a { color: inherit; }
|
|
66
|
+
|
|
67
|
+
.inkra-node-graph__node-shell { position: relative; min-inline-size: 168px; max-inline-size: min(280px, 72vw); }
|
|
68
|
+
.inkra-node-graph__node-paper { position: relative; min-inline-size: inherit; color: var(--inkra-ink); }
|
|
69
|
+
.inkra-node-graph__node-selection { position: absolute; z-index: -1; inset: 2px; pointer-events: none; }
|
|
70
|
+
.inkra-node-graph__node-content { position: relative; z-index: 1; display: grid; gap: var(--inkra-space-2); padding: var(--inkra-space-4); min-inline-size: 0; }
|
|
71
|
+
.inkra-node-graph__node-label { color: var(--inkra-ink); font-size: var(--inkra-text-md); line-height: var(--inkra-line-ui); overflow-wrap: anywhere; }
|
|
72
|
+
.inkra-node-graph__node-description { color: var(--inkra-ink-muted); font-size: var(--inkra-text-sm); line-height: var(--inkra-line-body); overflow-wrap: anywhere; }
|
|
73
|
+
.inkra-node-graph__node-body { min-inline-size: 0; }
|
|
74
|
+
.inkra-node-graph__node-focus { position: absolute; z-index: 3; inset: -4px; opacity: 0; pointer-events: none; }
|
|
75
|
+
.inkra-node-graph .react-flow__node:focus-visible .inkra-node-graph__node-focus { opacity: 1; }
|
|
76
|
+
|
|
77
|
+
.inkra-node-graph__port.react-flow__handle {
|
|
78
|
+
position: absolute;
|
|
79
|
+
display: grid;
|
|
80
|
+
place-items: center;
|
|
81
|
+
inline-size: var(--inkra-touch-target);
|
|
82
|
+
block-size: var(--inkra-touch-target);
|
|
83
|
+
border: 0;
|
|
84
|
+
background: transparent;
|
|
85
|
+
pointer-events: none;
|
|
86
|
+
transform: translate(-50%, -50%) scale(var(--inkra-node-graph-port-scale, 1));
|
|
87
|
+
z-index: 4;
|
|
88
|
+
}
|
|
89
|
+
.inkra-node-graph__port.react-flow__handle.connectionindicator { pointer-events: all; cursor: crosshair; }
|
|
90
|
+
.inkra-node-graph__port.react-flow__handle-left { inset-inline-start: 0; inset-block-start: 50%; }
|
|
91
|
+
.inkra-node-graph__port.react-flow__handle-right { inset-inline-start: 100%; inset-block-start: 50%; }
|
|
92
|
+
.inkra-node-graph__port.react-flow__handle-top { inset-inline-start: 50%; inset-block-start: 0; }
|
|
93
|
+
.inkra-node-graph__port.react-flow__handle-bottom { inset-inline-start: 50%; inset-block-start: 100%; }
|
|
94
|
+
.inkra-node-graph__port-visual { position: relative; display: grid; place-items: center; inline-size: 18px; block-size: 18px; pointer-events: none; }
|
|
95
|
+
.inkra-node-graph__port-mark,
|
|
96
|
+
.inkra-node-graph__port-feedback { position: absolute; inset: 0; pointer-events: none; }
|
|
97
|
+
.inkra-node-graph__port-feedback { opacity: 0; }
|
|
98
|
+
.inkra-node-graph__port:hover .inkra-node-graph__port-feedback,
|
|
99
|
+
.inkra-node-graph__port.connectingfrom .inkra-node-graph__port-feedback,
|
|
100
|
+
.inkra-node-graph__port.valid .inkra-node-graph__port-feedback { opacity: 1; }
|
|
101
|
+
|
|
102
|
+
.inkra-node-graph__edge-hit { pointer-events: stroke; }
|
|
103
|
+
.inkra-node-graph__edge-pencil { color: var(--inkra-ink-muted); pointer-events: none; }
|
|
104
|
+
.inkra-node-graph__edge-pencil[data-selected] { color: var(--inkra-accent-strong); }
|
|
105
|
+
.inkra-node-graph__edge-measure { stroke: transparent; stroke-width: 1; pointer-events: none; }
|
|
106
|
+
.inkra-node-graph__edge-fallback { stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; vector-effect: non-scaling-stroke; pointer-events: none; }
|
|
107
|
+
.inkra-node-graph__edge-pencil[data-pencil-ready] .inkra-node-graph__edge-fallback { visibility: hidden; }
|
|
108
|
+
.inkra-node-graph__edge-foreign-object { overflow: visible; pointer-events: none; }
|
|
109
|
+
.inkra-node-graph__edge-canvas { display: block; color: inherit; overflow: visible; pointer-events: none; }
|
|
110
|
+
.inkra-node-graph__edge-label { position: absolute; z-index: 5; max-inline-size: 180px; padding: 4px 8px; color: var(--inkra-ink); font-size: var(--inkra-text-sm); line-height: var(--inkra-line-ui); pointer-events: all; overflow-wrap: anywhere; }
|
|
111
|
+
.inkra-node-graph .react-flow__edgelabel-renderer { position: absolute; inset: 0; z-index: 5; inline-size: 100%; block-size: 100%; pointer-events: none; user-select: none; }
|
|
112
|
+
|
|
113
|
+
.inkra-node-graph__controls { display: flex; gap: var(--inkra-space-1); }
|
|
114
|
+
.inkra-node-graph__control { appearance: none; position: relative; display: inline-grid; place-items: center; inline-size: var(--inkra-touch-target); block-size: var(--inkra-touch-target); padding: 0; border: 0; color: var(--inkra-ink); cursor: pointer; }
|
|
115
|
+
.inkra-node-graph__control:focus { outline: none; }
|
|
116
|
+
.inkra-node-graph__control-focus { position: absolute; inset: 2px; opacity: 0; pointer-events: none; }
|
|
117
|
+
.inkra-node-graph__control:focus-visible .inkra-node-graph__control-focus { opacity: 1; }
|
|
118
|
+
|
|
119
|
+
.inkra-node-graph__empty { position: absolute; inset: 0; z-index: 6; display: grid; place-items: center; padding: var(--inkra-space-6); color: var(--inkra-ink-muted); text-align: center; pointer-events: none; }
|
|
120
|
+
.inkra-node-graph__ssr { position: absolute; inset: 0; display: grid; place-items: center; padding: var(--inkra-space-5); color: var(--inkra-ink); }
|
|
121
|
+
.inkra-node-graph__ssr ul { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--inkra-space-3); margin: 0; padding: 0; list-style: none; }
|
|
122
|
+
.inkra-node-graph__ssr li { padding: var(--inkra-space-3) var(--inkra-space-4); }
|
|
123
|
+
|
|
124
|
+
@media (max-width: 390px) {
|
|
125
|
+
.inkra-node-graph { block-size: 440px; min-block-size: 360px; }
|
|
126
|
+
.inkra-node-graph__node-shell { min-inline-size: 144px; max-inline-size: 68vw; }
|
|
127
|
+
.inkra-node-graph__node-content { padding: var(--inkra-space-3); }
|
|
128
|
+
.inkra-node-graph .react-flow__panel { margin: var(--inkra-space-2); }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@media (prefers-reduced-motion: reduce) {
|
|
132
|
+
.inkra-node-graph * { scroll-behavior: auto !important; transition-duration: 0s !important; animation-duration: 0s !important; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@media print, (forced-colors: active) {
|
|
136
|
+
.inkra-node-graph { border: 1px solid CanvasText; background: Canvas; color: CanvasText; }
|
|
137
|
+
.inkra-node-graph__edge-canvas { visibility: hidden; }
|
|
138
|
+
.inkra-node-graph__edge-fallback { visibility: visible !important; stroke: CanvasText; }
|
|
139
|
+
.inkra-node-graph__port-mark { outline: 1px solid CanvasText; outline-offset: -6px; }
|
|
140
|
+
.inkra-node-graph .react-flow__node:focus-visible .inkra-node-graph__node-shell,
|
|
141
|
+
.inkra-node-graph__control:focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.inkra-dependency-graph__node { position: relative; width: 100%; height: 100%; }
|
|
145
|
+
.inkra-dependency-graph__anchor.react-flow__handle { position: absolute; width: 0; height: 0; min-width: 0; min-height: 0; border: 0; opacity: 0; pointer-events: none; }
|
|
146
|
+
.inkra-dependency-graph__card, .inkra-dependency-graph__icon-button { box-sizing: border-box; width: 100%; height: 100%; padding: 14px 16px; border: 0; font: inherit; color: var(--inkra-ink); text-align: start; display: flex; flex-direction: column; gap: 6px; pointer-events: auto; }
|
|
147
|
+
.inkra-dependency-graph__card--custom { padding: 0; }
|
|
148
|
+
.inkra-dependency-graph__header { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
|
149
|
+
.inkra-dependency-graph__icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
|
|
150
|
+
.inkra-dependency-graph__title { appearance: none; background: none; border: 0; padding: 0; margin: 0; min-width: 0; flex: 1; font: inherit; color: inherit; text-align: start; font-weight: 600; cursor: pointer; }
|
|
151
|
+
.inkra-dependency-graph__title > * { max-width: 100%; }
|
|
152
|
+
.inkra-dependency-graph__actions { display: flex; align-items: center; flex: 0 0 auto; gap: 4px; margin-inline-start: auto; }
|
|
153
|
+
.inkra-dependency-graph__body { min-height: 0; overflow: auto; font-size: var(--inkra-text-sm); color: var(--inkra-ink-muted); }
|
|
154
|
+
.inkra-dependency-graph__footer { display: flex; align-items: center; gap: 8px; margin-top: auto; }
|
|
155
|
+
.inkra-dependency-graph__icon-button { align-items: center; justify-content: center; padding: 10px; cursor: pointer; }
|
|
156
|
+
.inkra-dependency-graph__card:focus-visible, .inkra-dependency-graph__title:focus-visible, .inkra-dependency-graph__icon-button:focus-visible { outline: 0; }
|
|
157
|
+
.inkra-dependency-graph__focus { --inkra-accent-strong: var(--inkra-focus); position: absolute !important; inset: -5px; pointer-events: none; opacity: 0; }
|
|
158
|
+
.inkra-dependency-graph__card:focus-visible > .inkra-dependency-graph__focus,
|
|
159
|
+
.inkra-dependency-graph__card--custom:focus-visible .inkra-dependency-graph__focus,
|
|
160
|
+
.inkra-dependency-graph__card:has(.inkra-dependency-graph__title:focus-visible) > .inkra-dependency-graph__focus,
|
|
161
|
+
.inkra-dependency-graph__icon-button:focus-visible > .inkra-dependency-graph__focus { opacity: 1; }
|
|
162
|
+
.inkra-dependency-graph__node[data-disabled] { opacity: .55; }
|
|
163
|
+
.inkra-dependency-graph__title:disabled, .inkra-dependency-graph__icon-button:disabled { cursor: not-allowed; }
|
|
164
|
+
.inkra-dependency-graph__status { font-size: var(--inkra-text-sm); color: var(--inkra-ink-muted); }
|
|
165
|
+
.inkra-dependency-graph__node[data-status="completed"] .inkra-dependency-graph__status { color: var(--inkra-success); }
|
|
166
|
+
.inkra-dependency-graph__node[data-status="available"] .inkra-dependency-graph__status,
|
|
167
|
+
.inkra-dependency-graph__node[data-status="active"] .inkra-dependency-graph__status { color: var(--inkra-accent-strong); }
|
|
168
|
+
.inkra-dependency-graph__highlight { position: absolute !important; inset: -3px; pointer-events: none; }
|
|
169
|
+
.inkra-dependency-graph__node[data-dimmed], .inkra-dependency-graph__edge[data-dimmed] { opacity: .5; }
|
|
170
|
+
.inkra-dependency-graph__edge { color: var(--inkra-ink-muted); }
|
|
171
|
+
.inkra-dependency-graph__edge[data-highlighted], .inkra-dependency-graph__edge--highlighted { color: var(--inkra-accent-strong); }
|
|
172
|
+
@media (forced-colors: active) {
|
|
173
|
+
.inkra-dependency-graph__card:focus-visible, .inkra-dependency-graph__title:focus-visible, .inkra-dependency-graph__icon-button:focus-visible { outline: var(--inkra-focus-width) solid Highlight; outline-offset: 4px; }
|
|
174
|
+
.inkra-dependency-graph__focus { display: none !important; }
|
|
175
|
+
.inkra-dependency-graph__node[data-dimmed], .inkra-dependency-graph__edge[data-dimmed] { opacity: 1; }
|
|
176
|
+
}
|
package/dist/styles/log.css
CHANGED
|
@@ -13,8 +13,6 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
.inkra-log[data-toolbar="true"] { grid-template-rows: auto minmax(var(--inkra-touch-target), 1fr); }
|
|
16
|
-
.inkra-log:has(> .inkra-log__latest) { grid-template-rows: minmax(var(--inkra-touch-target), 1fr) auto; }
|
|
17
|
-
.inkra-log[data-toolbar="true"]:has(> .inkra-log__latest) { grid-template-rows: auto minmax(var(--inkra-touch-target), 1fr) auto; }
|
|
18
16
|
|
|
19
17
|
.inkra-log__toolbar {
|
|
20
18
|
align-items: center;
|
|
@@ -158,21 +156,15 @@
|
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
.inkra-log__latest {
|
|
161
|
-
align-items: center;
|
|
162
159
|
display: flex;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
padding: var(--inkra-space-2) var(--inkra-space-3);
|
|
167
|
-
position: relative;
|
|
160
|
+
inset-block-end: clamp(0px, calc(100% - var(--inkra-touch-target)), var(--inkra-space-2));
|
|
161
|
+
inset-inline-end: var(--inkra-space-3);
|
|
162
|
+
position: absolute;
|
|
168
163
|
z-index: 2;
|
|
169
164
|
}
|
|
170
165
|
|
|
171
|
-
.inkra-log__latest-
|
|
172
|
-
|
|
173
|
-
inset-block-start: -4px;
|
|
174
|
-
inset-inline: 0;
|
|
175
|
-
position: absolute;
|
|
166
|
+
.inkra-log__latest .inkra-button__surface {
|
|
167
|
+
background: var(--inkra-paper);
|
|
176
168
|
}
|
|
177
169
|
|
|
178
170
|
@container (max-width: 36rem) {
|
|
@@ -188,11 +180,6 @@
|
|
|
188
180
|
.inkra-log__follow-control .inkra-button__content,
|
|
189
181
|
.inkra-log__follow-control .inkra-button__label,
|
|
190
182
|
.inkra-log__follow-control .inkra-pencil-text__source { min-inline-size: 0; white-space: normal; overflow-wrap: anywhere; }
|
|
191
|
-
.inkra-log__latest .inkra-button { inline-size: 100%; }
|
|
192
|
-
.inkra-log__latest .inkra-button__surface { height: auto; inline-size: 100%; max-inline-size: 100%; min-height: var(--inkra-button-visual-height); padding-block: var(--inkra-space-2); }
|
|
193
|
-
.inkra-log__latest .inkra-button__content,
|
|
194
|
-
.inkra-log__latest .inkra-button__label,
|
|
195
|
-
.inkra-log__latest .inkra-pencil-text__source { min-inline-size: 0; white-space: normal; overflow-wrap: anywhere; }
|
|
196
183
|
}
|
|
197
184
|
|
|
198
185
|
@media (forced-colors: active) {
|