@astryxdesign/lab 0.7.5-canary.fccd771 → 0.7.6-canary.0ef60dd
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 +2 -2
- package/astryx.integration.mjs +1 -1
- package/blocks/CommentPinShowcase.doc.mjs +1 -1
- package/blocks/CommentPinShowcase.tsx +2 -3
- package/dist/Chat/ChatUnreadDivider.js +1 -1
- package/dist/CircularProgress/CircularProgress.d.ts +2 -1
- package/dist/CircularProgress/CircularProgress.d.ts.map +1 -1
- package/dist/CircularProgress/CircularProgress.js +296 -19
- package/dist/CommentPin/CommentPin.d.ts +22 -7
- package/dist/CommentPin/CommentPin.d.ts.map +1 -1
- package/dist/CommentPin/CommentPin.js +151 -40
- package/dist/Comments/icons.d.ts.map +1 -1
- package/dist/Comments/icons.js +9 -2
- package/dist/DiffViewer/DiffViewer.d.ts +15 -4
- package/dist/DiffViewer/DiffViewer.d.ts.map +1 -1
- package/dist/DiffViewer/DiffViewer.js +31 -6
- package/dist/EmojiPicker/EmojiPicker.js +5 -8
- package/dist/InfoTip/InfoTip.d.ts +1 -1
- package/dist/InfoTip/InfoTip.js +1 -1
- package/dist/ListInput/ListInput.d.ts +1 -1
- package/dist/ListInput/ListInput.d.ts.map +1 -1
- package/dist/ListInput/ListInput.js +20 -1
- package/dist/LogStream/LogStream.d.ts.map +1 -1
- package/dist/LogStream/LogStream.js +2 -2
- package/dist/MobilePowerSearch/MobilePowerSearch.d.ts +49 -18
- package/dist/MobilePowerSearch/MobilePowerSearch.d.ts.map +1 -1
- package/dist/MobilePowerSearch/MobilePowerSearch.js +934 -366
- package/dist/MobilePowerSearch/MobilePowerSearchValueEditor.d.ts +10 -16
- package/dist/MobilePowerSearch/MobilePowerSearchValueEditor.d.ts.map +1 -1
- package/dist/MobilePowerSearch/MobilePowerSearchValueEditor.js +218 -113
- package/dist/MobilePowerSearch/mobilePowerSearchUtils.d.ts +23 -1
- package/dist/MobilePowerSearch/mobilePowerSearchUtils.d.ts.map +1 -1
- package/dist/MobilePowerSearch/mobilePowerSearchUtils.js +62 -2
- package/dist/MobileTokenizer/MobileTokenizer.d.ts +35 -10
- package/dist/MobileTokenizer/MobileTokenizer.d.ts.map +1 -1
- package/dist/MobileTokenizer/MobileTokenizer.js +714 -305
- package/dist/SVGIcon/SVGIcon.js +3 -3
- package/dist/Stat/Stat.js +2 -2
- package/dist/Tour/Tour.d.ts +1 -1
- package/dist/Tour/Tour.js +1 -1
- package/dist/Tour/TourStep.d.ts +1 -1
- package/dist/Tour/TourStep.js +1 -1
- package/dist/TransferList/TransferList.d.ts.map +1 -1
- package/dist/TransferList/TransferList.js +20 -0
- package/dist/index.js +3 -3
- package/dist/lab.css +43 -23
- package/dist/reorderStyles.d.ts +2 -2
- package/dist/reorderStyles.d.ts.map +1 -1
- package/dist/reorderStyles.js +2 -2
- package/package.json +10 -10
- package/src/Chat/ChatUnreadDivider.tsx +1 -1
- package/src/Chat/__tests__/ChatTypingIndicator.a11y.chromium.spec.ts +21 -8
- package/src/CircularProgress/CircularProgress.test.tsx +389 -10
- package/src/CircularProgress/CircularProgress.tsx +367 -23
- package/src/CircularProgress/__tests__/CircularProgress.a11y.chromium.spec.ts +22 -9
- package/src/CircularProgress/__tests__/CircularProgress.a11y.states.ts +2 -2
- package/src/CircularProgress/__tests__/CircularProgressMotion.a11y.chromium.spec.ts +886 -0
- package/src/CommentPin/CommentPin.doc.mjs +88 -38
- package/src/CommentPin/CommentPin.spec.md +99 -87
- package/src/CommentPin/CommentPin.test.tsx +275 -20
- package/src/CommentPin/CommentPin.tsx +190 -83
- package/src/Comments/Comments.test.tsx +10 -4
- package/src/Comments/icons.test.tsx +76 -0
- package/src/Comments/icons.tsx +9 -2
- package/src/DiffViewer/DiffViewer.doc.mjs +8 -2
- package/src/DiffViewer/DiffViewer.test.tsx +71 -0
- package/src/DiffViewer/DiffViewer.tsx +52 -9
- package/src/EmojiPicker/EmojiPicker.test.tsx +21 -0
- package/src/EmojiPicker/EmojiPicker.tsx +8 -8
- package/src/InfoTip/InfoTip.test.tsx +9 -4
- package/src/InfoTip/InfoTip.tsx +1 -1
- package/src/ListInput/ListInput.test.tsx +43 -3
- package/src/ListInput/ListInput.tsx +24 -1
- package/src/LogStream/LogStream.doc.mjs +1 -1
- package/src/LogStream/LogStream.tsx +3 -5
- package/src/MobilePowerSearch/MobilePowerSearch.doc.mjs +5 -5
- package/src/MobilePowerSearch/MobilePowerSearch.test.tsx +1092 -167
- package/src/MobilePowerSearch/MobilePowerSearch.tsx +1094 -368
- package/src/MobilePowerSearch/MobilePowerSearchValueEditor.tsx +303 -120
- package/src/MobilePowerSearch/mobilePowerSearchUtils.ts +68 -1
- package/src/MobileTokenizer/MobileTokenizer.doc.mjs +9 -5
- package/src/MobileTokenizer/MobileTokenizer.test.tsx +589 -5
- package/src/MobileTokenizer/MobileTokenizer.tsx +889 -350
- package/src/SVGIcon/SVGIcon.tsx +3 -3
- package/src/Stat/Stat.tsx +2 -2
- package/src/Tour/Tour.doc.mjs +1 -1
- package/src/Tour/Tour.tsx +1 -1
- package/src/Tour/TourStep.tsx +1 -1
- package/src/TransferList/TransferList.composition.test.tsx +4 -4
- package/src/TransferList/TransferList.test.tsx +36 -0
- package/src/TransferList/TransferList.tsx +24 -0
- package/src/TransferList/TransferListSelector.test.tsx +4 -4
- package/src/__tests__/ReorderGripPinchZoom.a11y.chromium.spec.ts +178 -0
- package/src/index.ts +3 -3
- package/src/reorderStyles.ts +7 -2
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Components in lab:
|
|
|
12
12
|
- Compose with `@astryxdesign/core` components (use the theme, follow naming conventions)
|
|
13
13
|
- Graduate to `@astryxdesign/core` after thorough engineering review
|
|
14
14
|
|
|
15
|
-
See the **[Component Lifecycle](https://github.com/
|
|
15
|
+
See the **[Component Lifecycle](https://github.com/meta/astryx/wiki/Component-Lifecycle)** wiki for how a component moves from lab → core (and the promotion gates), and the **[Component Hardening Protocol](https://github.com/meta/astryx/wiki/Component-Hardening-Protocol)** for the bar a component must clear to graduate.
|
|
16
16
|
|
|
17
17
|
## What's here vs what's in core
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ See the **[Component Lifecycle](https://github.com/facebook/astryx/wiki/Componen
|
|
|
22
22
|
|
|
23
23
|
## Promotion gate: accessibility
|
|
24
24
|
|
|
25
|
-
The [Accessibility Checklist](https://github.com/
|
|
25
|
+
The [Accessibility Checklist](https://github.com/meta/astryx/wiki/Accessibility-Checklist) is a hard requirement for graduating to core — every item, verified in the promotion PR, alongside the hardening bar linked above.
|
|
26
26
|
|
|
27
27
|
Lab components get the same scan coverage as core (the `pr-a11y` axe audit and the weekly full scan run against lab too), but a11y findings on a lab component do not block lab merges — they block **promotion**. Iterate freely in lab; clear the checklist to graduate.
|
|
28
28
|
|
package/astryx.integration.mjs
CHANGED
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
name: 'Avatar-first annotations',
|
|
7
7
|
displayName: 'Avatar-first annotations',
|
|
8
8
|
description:
|
|
9
|
-
'Caller-positioned comment pins over media and in a content margin. Every coordinate maps to the pin’s logical bottom-start corner. The selected group caps its participants at three circles (two Avatars plus
|
|
9
|
+
'Caller-positioned comment pins over media and in a content margin. Every coordinate maps to the pin’s logical bottom-start corner. The selected group caps its participants at three circles (two Avatars plus an ellipsis) and ends with its total and an accent unread dot; the unread margin pin shows the dot too. Other pins dim without becoming disabled, activating the selected pin again clears the selection, and no pin dims while nothing is selected.',
|
|
10
10
|
exampleFor: 'CommentPin',
|
|
11
11
|
isReady: true,
|
|
12
12
|
isShowcase: true,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* participant/thread/unread counts, and controlled selected/dimmed state
|
|
8
8
|
* that may be empty
|
|
9
9
|
* @output A grouped pin with a three-circle participant cap and an unread
|
|
10
|
-
*
|
|
10
|
+
* dot, a single pin, and an unread margin pin; activating the selected
|
|
11
11
|
* pin clears the selection, and non-selected pins dim only while another pin
|
|
12
12
|
* is selected
|
|
13
13
|
* @position CommentPin docsite showcase and copyable Lab block
|
|
@@ -160,9 +160,8 @@ export default function CommentPinShowcase() {
|
|
|
160
160
|
xstyle={styles.groupedMarker}
|
|
161
161
|
/>
|
|
162
162
|
<CommentPin
|
|
163
|
-
label="Open
|
|
163
|
+
label="Open annotation from Mira"
|
|
164
164
|
participants={[PEOPLE.mira]}
|
|
165
|
-
isResolved
|
|
166
165
|
isSelected={selected === 'resolved'}
|
|
167
166
|
isDimmed={selected != null && selected !== 'resolved'}
|
|
168
167
|
onClick={() => toggle('resolved')}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file CircularProgress.tsx
|
|
3
|
-
* @input Uses React
|
|
3
|
+
* @input Uses React (useId, refs, layout effects), stylex, SVG, Web
|
|
4
|
+
* Animations, color/spacing/duration/ease tokens
|
|
4
5
|
* @output Exports CircularProgress component, CircularProgressProps, CircularProgressSize, CircularProgressVariant types
|
|
5
6
|
* @position Core implementation; consumed by index.ts
|
|
6
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CircularProgress.d.ts","sourceRoot":"","sources":["../../src/CircularProgress/CircularProgress.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"CircularProgress.d.ts","sourceRoot":"","sources":["../../src/CircularProgress/CircularProgress.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAYrE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAGlD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC;CACf;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAWtD,MAAM,WAAW,qBAAsB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACtE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAClC;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAmWD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAS,EACT,GAAS,EACT,KAAK,EACL,aAAoB,EACpB,aAAqB,EACrB,gBAA0C,EAC1C,QAAQ,EACR,IAAW,EACX,OAAkB,EAClB,eAAuB,EACvB,UAAkB,EAClB,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,UAAU,EACzB,GAAG,EACH,GAAG,IAAI,EACR,EAAE,qBAAqB,+BA+QvB;yBAjSe,gBAAgB"}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file CircularProgress.tsx
|
|
7
|
-
* @input Uses React
|
|
7
|
+
* @input Uses React (useId, refs, layout effects), stylex, SVG, Web
|
|
8
|
+
* Animations, color/spacing/duration/ease tokens
|
|
8
9
|
* @output Exports CircularProgress component, CircularProgressProps, CircularProgressSize, CircularProgressVariant types
|
|
9
10
|
* @position Core implementation; consumed by index.ts
|
|
10
11
|
*
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
* - /packages/lab/src/CircularProgress/index.ts (exports if types change)
|
|
15
16
|
* - /apps/storybook/stories/CircularProgress.stories.tsx (storybook stories)
|
|
16
17
|
*/
|
|
17
|
-
import { useId } from 'react';
|
|
18
|
+
import { useId, useLayoutEffect, useRef } from 'react';
|
|
18
19
|
import * as stylex from '@stylexjs/stylex';
|
|
19
20
|
import '@astryxdesign/core/theme/tokens.stylex';
|
|
20
21
|
import { colorVars, durationVars, easeVars, fontWeightVars, spacingVars, typeScaleVars } from '@astryxdesign/core/theme/tokens.stylex';
|
|
@@ -35,6 +36,8 @@ import { themeProps } from '@astryxdesign/core/utils';
|
|
|
35
36
|
* ```
|
|
36
37
|
*/
|
|
37
38
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
39
|
+
/** How far a turning layer's drawing reaches past the ring's box, in px. */
|
|
40
|
+
const FILL_BLEED = 2;
|
|
38
41
|
const SIZE_CONFIG = {
|
|
39
42
|
sm: {
|
|
40
43
|
diameter: 32,
|
|
@@ -83,9 +86,11 @@ const styles = {
|
|
|
83
86
|
$$css: true
|
|
84
87
|
},
|
|
85
88
|
fill: {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
kVAEAm: "x10l6tqk",
|
|
90
|
+
kpwlN0: "x10a8y8t",
|
|
91
|
+
kVQacm: "xb3r6kr",
|
|
92
|
+
kfzvcC: "x47corl",
|
|
93
|
+
k1ekBW: "x13b0p5u",
|
|
89
94
|
kIyJzY: "x80gvsz",
|
|
90
95
|
kAMwcw: "xlr8y92",
|
|
91
96
|
$$css: true
|
|
@@ -106,15 +111,15 @@ const variantStyles = {
|
|
|
106
111
|
$$css: true
|
|
107
112
|
},
|
|
108
113
|
success: {
|
|
109
|
-
kjVXCG: "
|
|
114
|
+
kjVXCG: "x88o7k4",
|
|
110
115
|
$$css: true
|
|
111
116
|
},
|
|
112
117
|
warning: {
|
|
113
|
-
kjVXCG: "
|
|
118
|
+
kjVXCG: "x16d96h5",
|
|
114
119
|
$$css: true
|
|
115
120
|
},
|
|
116
121
|
error: {
|
|
117
|
-
kjVXCG: "
|
|
122
|
+
kjVXCG: "xqb94c9",
|
|
118
123
|
$$css: true
|
|
119
124
|
},
|
|
120
125
|
neutral: {
|
|
@@ -153,6 +158,129 @@ function defaultFormatValueLabel(value, max) {
|
|
|
153
158
|
return `${pct}%`;
|
|
154
159
|
}
|
|
155
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Each half-ring's turn and the leading cap's for a filled share `p` (0–1),
|
|
163
|
+
* and which parts show. A half-ring parked at the edge of its half (the right
|
|
164
|
+
* one at 0, the left one until the value passes half) is hidden, so its
|
|
165
|
+
* antialiased end never leaves a hairline at 6 o'clock.
|
|
166
|
+
*/
|
|
167
|
+
function fillTurns(p) {
|
|
168
|
+
const turn = 360 * p;
|
|
169
|
+
return {
|
|
170
|
+
right: Math.min(Math.max(turn, 0), 180),
|
|
171
|
+
left: Math.min(Math.max(turn, 180), 360),
|
|
172
|
+
cap: turn,
|
|
173
|
+
rightShown: p > 0 ? 1 : 0,
|
|
174
|
+
leftShown: p > 0.5 ? 1 : 0,
|
|
175
|
+
caps: p > 0 ? 1 : 0
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** A CSS time (`410ms`, `0.41s`, or a list's first entry) in milliseconds. */
|
|
180
|
+
function parseTime(value) {
|
|
181
|
+
const first = value.split(',')[0]?.trim() ?? '';
|
|
182
|
+
const amount = Number.parseFloat(first);
|
|
183
|
+
if (!Number.isFinite(amount)) {
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
return first.endsWith('ms') ? amount : amount * 1000;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The theme's easing as a function of time (0–1), evaluated by the browser
|
|
191
|
+
* itself through a detached animation, so any CSS easing works. It runs only
|
|
192
|
+
* where Web Animations exists (its caller returns first otherwise), on the
|
|
193
|
+
* fill's computed `transition-timing-function`: one easing, which a browser
|
|
194
|
+
* always reports as valid CSS.
|
|
195
|
+
*/
|
|
196
|
+
function easingOf(easing) {
|
|
197
|
+
const effect = new KeyframeEffect(null, null, {
|
|
198
|
+
duration: 1000,
|
|
199
|
+
easing,
|
|
200
|
+
fill: 'both'
|
|
201
|
+
});
|
|
202
|
+
const probe = new Animation(effect);
|
|
203
|
+
return t => {
|
|
204
|
+
probe.currentTime = t * 1000;
|
|
205
|
+
return effect.getComputedTiming().progress ?? t;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Keyframes for a change from `from` to `to` (filled shares), sampled once a
|
|
211
|
+
* frame on the theme's easing, with an extra sample exactly where the arc
|
|
212
|
+
* crosses 12 or 6 o'clock so each half stops and starts on time.
|
|
213
|
+
*/
|
|
214
|
+
function fillKeyframes(from, to, duration, ease) {
|
|
215
|
+
const count = Math.max(8, Math.ceil(duration / (1000 / 60)));
|
|
216
|
+
const grid = Array.from({
|
|
217
|
+
length: count + 1
|
|
218
|
+
}, (_, i) => i / count);
|
|
219
|
+
const at = t => from + (to - from) * ease(t);
|
|
220
|
+
// Where the eased path passes half (p = 0.5): a sample there, and one just
|
|
221
|
+
// after it, so the halves hand over on time and the left half's
|
|
222
|
+
// visibility steps rather than fades.
|
|
223
|
+
const crossings = [];
|
|
224
|
+
for (let i = 0; i < count; i++) {
|
|
225
|
+
const a = grid[i];
|
|
226
|
+
const b = grid[i + 1];
|
|
227
|
+
const fa = at(a) - 0.5;
|
|
228
|
+
const fb = at(b) - 0.5;
|
|
229
|
+
if (fa === 0) {
|
|
230
|
+
crossings.push(a);
|
|
231
|
+
} else if (fa * fb < 0) {
|
|
232
|
+
let low = a;
|
|
233
|
+
let high = b;
|
|
234
|
+
for (let k = 0; k < 24; k++) {
|
|
235
|
+
const mid = (low + high) / 2;
|
|
236
|
+
if ((at(low) - 0.5) * (at(mid) - 0.5) <= 0) {
|
|
237
|
+
high = mid;
|
|
238
|
+
} else {
|
|
239
|
+
low = mid;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
crossings.push((low + high) / 2);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const times = [...new Set([...grid, ...crossings, ...crossings.map(c => c + 1e-4).filter(t => t < 1)])].sort((x, y) => x - y);
|
|
246
|
+
const frames = times.map(t => ({
|
|
247
|
+
t,
|
|
248
|
+
...fillTurns(t === 1 ? to : at(t))
|
|
249
|
+
}));
|
|
250
|
+
// The sample at a crossing sits exactly on half, where the left half reads
|
|
251
|
+
// as hidden. Give it the side the arc is leaving instead, so the left half
|
|
252
|
+
// steps at once both ways: shrinking through half, it used to fade out
|
|
253
|
+
// from the sample before, while it still drew part of the arc (a lighter
|
|
254
|
+
// band between the end cap and 6 o'clock for a frame).
|
|
255
|
+
const onHalf = new Set(crossings);
|
|
256
|
+
for (let i = 1; i < frames.length; i += 1) {
|
|
257
|
+
if (onHalf.has(frames[i].t) || frames[i].t === 1 && to === 0.5) {
|
|
258
|
+
frames[i].leftShown = frames[i - 1].leftShown;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
right: frames.map(f => ({
|
|
263
|
+
offset: f.t,
|
|
264
|
+
transform: `rotate(${f.right}deg)`,
|
|
265
|
+
opacity: f.rightShown
|
|
266
|
+
})),
|
|
267
|
+
left: frames.map(f => ({
|
|
268
|
+
offset: f.t,
|
|
269
|
+
transform: `rotate(${f.left}deg)`,
|
|
270
|
+
opacity: f.leftShown
|
|
271
|
+
})),
|
|
272
|
+
cap: frames.map(f => ({
|
|
273
|
+
offset: f.t,
|
|
274
|
+
transform: `rotate(${f.cap}deg)`,
|
|
275
|
+
opacity: f.caps
|
|
276
|
+
})),
|
|
277
|
+
start: frames.map(f => ({
|
|
278
|
+
offset: f.t,
|
|
279
|
+
opacity: f.caps
|
|
280
|
+
}))
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
156
284
|
/**
|
|
157
285
|
* A circular/radial progress indicator that shows completion as a ring.
|
|
158
286
|
*
|
|
@@ -205,7 +333,6 @@ export function CircularProgress({
|
|
|
205
333
|
const safeMax = Number.isFinite(max) ? max : 0;
|
|
206
334
|
const clampedValue = Math.min(Math.max(0, safeValue), safeMax);
|
|
207
335
|
const percentage = safeMax > 0 ? clampedValue / safeMax : 0;
|
|
208
|
-
const dashoffset = circumference * (1 - percentage);
|
|
209
336
|
const valueText = formatValueLabel(clampedValue, safeMax);
|
|
210
337
|
const center = diameter / 2;
|
|
211
338
|
const showLabel = !isLabelHidden;
|
|
@@ -213,6 +340,89 @@ export function CircularProgress({
|
|
|
213
340
|
const centerContent = children != null ? children : showValueLabel ? valueText : null;
|
|
214
341
|
const fillVariant = isDisabled ? 'disabled' : variant;
|
|
215
342
|
const trackVariant = isDisabled ? 'neutral' : variant;
|
|
343
|
+
|
|
344
|
+
// A value change turns the fill's layers with Web Animations on the
|
|
345
|
+
// compositor, from wherever the arc is drawn now (mid-change, the running
|
|
346
|
+
// change's own curve says where) to the new value, on the theme's
|
|
347
|
+
// duration and easing. What renders is always the resting state, so a
|
|
348
|
+
// first render, a render without script, or a skipped animation shows the
|
|
349
|
+
// value.
|
|
350
|
+
const fillRef = useRef(null);
|
|
351
|
+
const rightRef = useRef(null);
|
|
352
|
+
const leftRef = useRef(null);
|
|
353
|
+
const capRef = useRef(null);
|
|
354
|
+
const startRef = useRef(null);
|
|
355
|
+
// Where the arc is drawn: the value at rest, or, when a change is
|
|
356
|
+
// interrupted, the point its own curve has reached. A change's cleanup
|
|
357
|
+
// records it and releases the change's animations (react-component-runtime
|
|
358
|
+
// INV3), so the next change starts from there and an unmount or a switch to
|
|
359
|
+
// indeterminate leaves nothing running. While indeterminate no value is
|
|
360
|
+
// drawn (null): the ring draws its own arc, not the value it holds, so the
|
|
361
|
+
// next value draws at once instead of sweeping from that held value.
|
|
362
|
+
const drawn = useRef(percentage);
|
|
363
|
+
useLayoutEffect(() => {
|
|
364
|
+
const from = drawn.current;
|
|
365
|
+
if (isIndeterminate) {
|
|
366
|
+
drawn.current = null;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (from === null || from === percentage) {
|
|
370
|
+
drawn.current = percentage;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const fill = fillRef.current;
|
|
374
|
+
const layers = [rightRef.current, leftRef.current, capRef.current, startRef.current];
|
|
375
|
+
drawn.current = percentage;
|
|
376
|
+
if (fill == null || layers.some(layer => typeof layer?.animate !== 'function')) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const style = getComputedStyle(fill);
|
|
380
|
+
const duration = parseTime(style.transitionDuration);
|
|
381
|
+
if (duration <= 0) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const ease = easingOf(style.transitionTimingFunction);
|
|
385
|
+
const frames = fillKeyframes(from, percentage, duration, ease);
|
|
386
|
+
const timing = {
|
|
387
|
+
duration,
|
|
388
|
+
easing: 'linear',
|
|
389
|
+
fill: 'backwards'
|
|
390
|
+
};
|
|
391
|
+
const [right, left, cap, start] = layers;
|
|
392
|
+
const animations = [right.animate(frames.right, timing), left.animate(frames.left, timing), cap.animate(frames.cap, timing), start.animate(frames.start, timing)];
|
|
393
|
+
return () => {
|
|
394
|
+
const [first] = animations;
|
|
395
|
+
const progress = first.playState !== 'finished' ? Number(first.effect?.getComputedTiming().progress ?? 1) : 1;
|
|
396
|
+
// `progress` is the change's linear time fraction; the arc is drawn on
|
|
397
|
+
// its easing, so the eased position is where it is.
|
|
398
|
+
drawn.current = from + (percentage - from) * ease(progress);
|
|
399
|
+
for (const animation of animations) {
|
|
400
|
+
animation.cancel();
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}, [percentage, isIndeterminate]);
|
|
404
|
+
const turns = fillTurns(percentage);
|
|
405
|
+
// One half of the ring's stroke, from 6 o'clock clockwise to 12 (the left
|
|
406
|
+
// half) before its layer turns; butt ends, the caps are drawn apart.
|
|
407
|
+
const bleedBox = `${-FILL_BLEED} ${-FILL_BLEED} ${diameter + 2 * FILL_BLEED} ${diameter + 2 * FILL_BLEED}`;
|
|
408
|
+
const halfRing = /*#__PURE__*/_jsx("svg", {
|
|
409
|
+
width: diameter + 2 * FILL_BLEED,
|
|
410
|
+
height: diameter + 2 * FILL_BLEED,
|
|
411
|
+
viewBox: bleedBox,
|
|
412
|
+
...{
|
|
413
|
+
className: "x1lliihq xbh8q5q xy6n8ys"
|
|
414
|
+
},
|
|
415
|
+
children: /*#__PURE__*/_jsx("circle", {
|
|
416
|
+
cx: center,
|
|
417
|
+
cy: center,
|
|
418
|
+
r: radius,
|
|
419
|
+
strokeWidth: strokeWidth,
|
|
420
|
+
strokeDasharray: `${circumference / 2} ${circumference}`,
|
|
421
|
+
transform: `rotate(90 ${center} ${center})`
|
|
422
|
+
})
|
|
423
|
+
});
|
|
424
|
+
// A round cap at 12 o'clock on the ring's centerline (a zero-length stroke).
|
|
425
|
+
const capPath = `M ${center} ${strokeWidth / 2} h 0`;
|
|
216
426
|
return /*#__PURE__*/_jsxs("div", {
|
|
217
427
|
ref: ref,
|
|
218
428
|
...mergeProps(themeProps('circular-progress', {
|
|
@@ -274,16 +484,83 @@ export function CircularProgress({
|
|
|
274
484
|
cy: center,
|
|
275
485
|
r: radius,
|
|
276
486
|
strokeWidth: strokeWidth
|
|
277
|
-
}) :
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
487
|
+
}) : null]
|
|
488
|
+
}), isIndeterminate ? null :
|
|
489
|
+
/*#__PURE__*/
|
|
490
|
+
// The determinate fill (see `styles.fill`). Its stroke color is
|
|
491
|
+
// set on the theming target and inherited by every part.
|
|
492
|
+
_jsxs("span", {
|
|
493
|
+
ref: fillRef,
|
|
494
|
+
"aria-hidden": "true",
|
|
495
|
+
...mergeProps(themeProps('circular-progress-fill', {
|
|
496
|
+
variant: fillVariant
|
|
497
|
+
}), stylex.props(styles.fill, variantStyles[fillVariant])),
|
|
498
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
499
|
+
...{
|
|
500
|
+
className: "x10l6tqk x13vifvy x3hqpx7 x5yr21d xb3r6kr x1nrll8i"
|
|
501
|
+
},
|
|
502
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
503
|
+
ref: rightRef,
|
|
504
|
+
...{
|
|
505
|
+
className: "x10l6tqk x13vifvy x75ztot x5yr21d x1q1rmc8 xb5nubl"
|
|
506
|
+
},
|
|
507
|
+
style: {
|
|
508
|
+
transform: `rotate(${turns.right}deg)`,
|
|
509
|
+
opacity: turns.rightShown
|
|
510
|
+
},
|
|
511
|
+
children: halfRing
|
|
512
|
+
})
|
|
513
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
514
|
+
...{
|
|
515
|
+
className: "x10l6tqk x13vifvy x3hqpx7 x5yr21d xb3r6kr xu96u03"
|
|
516
|
+
},
|
|
517
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
518
|
+
ref: leftRef,
|
|
519
|
+
...{
|
|
520
|
+
className: "x10l6tqk x13vifvy x75ztot x5yr21d x1q1rmc8 xu96u03"
|
|
521
|
+
},
|
|
522
|
+
style: {
|
|
523
|
+
transform: `rotate(${turns.left}deg)`,
|
|
524
|
+
opacity: turns.leftShown
|
|
525
|
+
},
|
|
526
|
+
children: halfRing
|
|
527
|
+
})
|
|
528
|
+
}), /*#__PURE__*/_jsx("svg", {
|
|
529
|
+
width: diameter,
|
|
530
|
+
height: diameter,
|
|
531
|
+
viewBox: `0 0 ${diameter} ${diameter}`,
|
|
532
|
+
...{
|
|
533
|
+
className: "x10l6tqk x10a8y8t x1q1rmc8 x1lliihq xbh8q5q x1owpc8m"
|
|
534
|
+
},
|
|
535
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
536
|
+
ref: startRef,
|
|
537
|
+
d: capPath,
|
|
538
|
+
strokeWidth: strokeWidth,
|
|
539
|
+
style: {
|
|
540
|
+
opacity: turns.caps
|
|
541
|
+
}
|
|
542
|
+
})
|
|
543
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
544
|
+
ref: capRef,
|
|
545
|
+
...{
|
|
546
|
+
className: "x10l6tqk x10a8y8t x1q1rmc8"
|
|
547
|
+
},
|
|
548
|
+
style: {
|
|
549
|
+
transform: `rotate(${turns.cap}deg)`,
|
|
550
|
+
opacity: turns.caps
|
|
551
|
+
},
|
|
552
|
+
children: /*#__PURE__*/_jsx("svg", {
|
|
553
|
+
width: diameter + 2 * FILL_BLEED,
|
|
554
|
+
height: diameter + 2 * FILL_BLEED,
|
|
555
|
+
viewBox: bleedBox,
|
|
556
|
+
...{
|
|
557
|
+
className: "x1owpc8m x1lliihq xbh8q5q xy6n8ys"
|
|
558
|
+
},
|
|
559
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
560
|
+
d: capPath,
|
|
561
|
+
strokeWidth: strokeWidth
|
|
562
|
+
})
|
|
563
|
+
})
|
|
287
564
|
})]
|
|
288
565
|
}), centerContent != null && /*#__PURE__*/_jsx("div", {
|
|
289
566
|
...{
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { BaseProps } from '@astryxdesign/core';
|
|
2
|
+
/** Pin heights on the shared element-size scale: 28, 32, or 36px. */
|
|
3
|
+
export type CommentPinSize = 'sm' | 'md' | 'lg';
|
|
2
4
|
/** Identity data for one representative shown inside a CommentPin. */
|
|
3
5
|
export interface CommentPinParticipant {
|
|
4
6
|
/** Stable identity used to remove duplicate representatives. */
|
|
@@ -21,10 +23,17 @@ export interface CommentPinProps extends Omit<BaseProps<HTMLButtonElement>, 'chi
|
|
|
21
23
|
* Ordered participants, lead first. A single marker shows the first. A
|
|
22
24
|
* grouped marker shows distinct IDs as an AvatarGroup of at most three
|
|
23
25
|
* circles: every participant when three or fewer are distinct, otherwise the
|
|
24
|
-
* first two plus
|
|
25
|
-
* the overflow
|
|
26
|
+
* first two plus an ellipsis overflow circle. Pass every distinct
|
|
27
|
+
* participant so the overflow appears only when more than three people
|
|
28
|
+
* participate.
|
|
26
29
|
*/
|
|
27
30
|
participants: readonly [CommentPinParticipant, ...CommentPinParticipant[]];
|
|
31
|
+
/**
|
|
32
|
+
* Pin height on the element-size scale: `sm` 28px, `md` 32px, `lg` 36px,
|
|
33
|
+
* with 22px, 24px, and 28px participant circles.
|
|
34
|
+
* @default 'sm'
|
|
35
|
+
*/
|
|
36
|
+
size?: CommentPinSize;
|
|
28
37
|
/**
|
|
29
38
|
* Total number of threads represented by this marker. Values above one use
|
|
30
39
|
* the grouped presentation and show the exact total.
|
|
@@ -33,8 +42,9 @@ export interface CommentPinProps extends Omit<BaseProps<HTMLButtonElement>, 'chi
|
|
|
33
42
|
threadCount?: number;
|
|
34
43
|
/**
|
|
35
44
|
* Number of represented threads with unread activity. When above zero, an
|
|
36
|
-
* accent
|
|
37
|
-
*
|
|
45
|
+
* accent status dot appears at the end of the pin: after the thread total,
|
|
46
|
+
* or after the participant on a single pin. Values above `threadCount` are
|
|
47
|
+
* capped.
|
|
38
48
|
* @default 0
|
|
39
49
|
*/
|
|
40
50
|
unreadCount?: number;
|
|
@@ -45,7 +55,11 @@ export interface CommentPinProps extends Omit<BaseProps<HTMLButtonElement>, 'chi
|
|
|
45
55
|
* disabling it, so it remains available to switch selection.
|
|
46
56
|
*/
|
|
47
57
|
isDimmed?: boolean;
|
|
48
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Whether every thread represented by this pin is resolved. A resolved pin
|
|
60
|
+
* renders nothing unless it is selected, so resolved discussion leaves the
|
|
61
|
+
* content while a thread the reader opened keeps its anchor.
|
|
62
|
+
*/
|
|
49
63
|
isResolved?: boolean;
|
|
50
64
|
/** Whether activation is unavailable. */
|
|
51
65
|
isDisabled?: boolean;
|
|
@@ -56,7 +70,8 @@ export interface CommentPinProps extends Omit<BaseProps<HTMLButtonElement>, 'chi
|
|
|
56
70
|
* The caller owns grouping, representative selection, coordinates, collision
|
|
57
71
|
* handling, and the content opened by activation. Absolute placement maps the
|
|
58
72
|
* host anchor to the pin's logical bottom-start corner. The pin only owns its
|
|
59
|
-
* compact button surface and controlled visual states.
|
|
73
|
+
* compact button surface and controlled visual states. `size` sets its height
|
|
74
|
+
* on the element-size scale.
|
|
60
75
|
*
|
|
61
76
|
* @example
|
|
62
77
|
* ```
|
|
@@ -79,7 +94,7 @@ export interface CommentPinProps extends Omit<BaseProps<HTMLButtonElement>, 'chi
|
|
|
79
94
|
* />
|
|
80
95
|
* ```
|
|
81
96
|
*/
|
|
82
|
-
export declare function CommentPin({ label, participants, threadCount, unreadCount, isSelected, isDimmed, isResolved, isDisabled, ref, xstyle, className, style, ...rest }: CommentPinProps): React.ReactNode;
|
|
97
|
+
export declare function CommentPin({ label, participants, size, threadCount, unreadCount, isSelected, isDimmed, isResolved, isDisabled, ref, xstyle, className, style, ...rest }: CommentPinProps): React.ReactNode;
|
|
83
98
|
export declare namespace CommentPin {
|
|
84
99
|
var displayName: string;
|
|
85
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentPin.d.ts","sourceRoot":"","sources":["../../src/CommentPin/CommentPin.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommentPin.d.ts","sourceRoot":"","sources":["../../src/CommentPin/CommentPin.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AA4BlD,qEAAqE;AACrE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,sEAAsE;AACtE,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,iBAAiB,CAAC,EAC5B,UAAU,CACX;IACC,0CAA0C;IAC1C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,CAAC,qBAAqB,EAAE,GAAG,qBAAqB,EAAE,CAAC,CAAC;IAC3E;;;;OAIG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA6OD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,YAAY,EACZ,IAAW,EACX,WAAe,EACf,WAAe,EACf,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAClB,UAAkB,EAClB,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,eAAe,GAAG,KAAK,CAAC,SAAS,CAwLnC;yBAvMe,UAAU"}
|