@kanso-labs/react-native-kanso-ui 0.1.1 → 0.3.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/dist/button/index.d.ts +32 -2
- package/dist/button/index.js +38 -26
- package/dist/button/index.js.map +1 -1
- package/dist/button/pending.js +144 -0
- package/dist/button/pending.js.map +1 -0
- package/dist/button/slots.js +7 -0
- package/dist/button/slots.js.map +1 -0
- package/dist/components/avatar/index.d.ts +2 -2
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/button/index.d.ts +3 -3
- package/dist/components/button/index.js +159 -111
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/card/index.d.ts +2 -2
- package/dist/components/card/index.js.map +1 -1
- package/dist/components/checkbox/index.d.ts +2 -2
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/chip/index.d.ts +2 -2
- package/dist/components/chip/index.js.map +1 -1
- package/dist/components/chip-group/index.d.ts +3 -3
- package/dist/components/chip-group/index.js.map +1 -1
- package/dist/components/copy-field/index.d.ts +2 -2
- package/dist/components/copy-field/index.js.map +1 -1
- package/dist/components/dialog/index.d.ts +131 -0
- package/dist/components/dialog/index.js +861 -0
- package/dist/components/dialog/index.js.map +1 -0
- package/dist/components/icon-button/index.d.ts +3 -3
- package/dist/components/icon-button/index.js +165 -109
- package/dist/components/icon-button/index.js.map +1 -1
- package/dist/components/link/index.js +1 -1
- package/dist/components/link/index.js.map +1 -1
- package/dist/components/meter/index.d.ts +73 -0
- package/dist/components/meter/index.js +179 -0
- package/dist/components/meter/index.js.map +1 -0
- package/dist/components/number-field/index.d.ts +3 -3
- package/dist/components/number-field/index.js +1 -9
- package/dist/components/number-field/index.js.map +1 -1
- package/dist/components/product-icon/index.d.ts +2 -2
- package/dist/components/product-icon/index.js.map +1 -1
- package/dist/components/progress-indicator/index.d.ts +113 -0
- package/dist/components/progress-indicator/index.js +1024 -0
- package/dist/components/progress-indicator/index.js.map +1 -0
- package/dist/components/radio-group/index.d.ts +3 -3
- package/dist/components/radio-group/index.js.map +1 -1
- package/dist/components/search-field/index.d.ts +3 -3
- package/dist/components/search-field/index.js.map +1 -1
- package/dist/components/segmented-button/index.d.ts +3 -3
- package/dist/components/segmented-button/index.js.map +1 -1
- package/dist/components/snackbar/index.d.ts +120 -0
- package/dist/components/snackbar/index.js +540 -0
- package/dist/components/snackbar/index.js.map +1 -0
- package/dist/components/switch/index.d.ts +2 -2
- package/dist/components/switch/index.js.map +1 -1
- package/dist/components/text-field/index.d.ts +3 -3
- package/dist/components/text-field/index.js.map +1 -1
- package/dist/field/index.d.ts +1 -1
- package/dist/field/index.js.map +1 -1
- package/dist/form/index.d.ts +1 -1
- package/dist/form/index.js.map +1 -1
- package/dist/glyphs/hidden.js +13 -0
- package/dist/glyphs/hidden.js.map +1 -0
- package/dist/glyphs/index.js +2 -8
- package/dist/glyphs/index.js.map +1 -1
- package/dist/i18n/index.js +23 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/messages.js +41 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -3
- package/dist/indicator/index.d.ts +15 -0
- package/dist/indicator/index.js +256 -0
- package/dist/indicator/index.js.map +1 -0
- package/dist/indicator/motion.js +86 -0
- package/dist/indicator/motion.js.map +1 -0
- package/dist/indicator/paths.js +72 -0
- package/dist/indicator/paths.js.map +1 -0
- package/dist/indicator/styles.js +62 -0
- package/dist/indicator/styles.js.map +1 -0
- package/dist/mark/index.d.ts +1 -1
- package/dist/mark/index.js.map +1 -1
- package/dist/number/locale.js +14 -0
- package/dist/number/locale.js.map +1 -0
- package/dist/overlay/host.js +65 -2
- package/dist/overlay/host.js.map +1 -1
- package/dist/press/index.js +63 -63
- package/dist/press/index.js.map +1 -1
- package/dist/theme/resolve.js +1 -1
- package/dist/theme/resolve.js.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","names":["LibraryMessages","loading","MESSAGES","Readonly","Record"],"sources":["../../src/i18n/messages.ts"],"sourcesContent":["// The words the library writes itself: the pending ring's name. Each one\n// stands behind a prop that names it otherwise, `pendingLabel` for this one.\n//\n// Copied from kanso-ui's src/i18n/messages.ts, one entry per locale React\n// Aria ships, and kept in step with it by hand: the web library publishes no\n// copy of its table, and a word that differs between the two would name the\n// same ring two ways. The translations were drafted for that table rather\n// than taken from a translation service, and each one wants a native\n// speaker's eye before it is trusted; a call site can always pass its own\n// words through the prop.\n\ntype LibraryMessages = {\n /** What a pending button's ring is called. */\n readonly loading: string\n}\n\nconst MESSAGES: Readonly<Record<string, LibraryMessages>> = {\n 'ar-AE': { loading: 'جارٍ التحميل' },\n 'bg-BG': { loading: 'Зареждане' },\n 'cs-CZ': { loading: 'Načítání' },\n 'da-DK': { loading: 'Indlæser' },\n 'de-DE': { loading: 'Wird geladen' },\n 'el-GR': { loading: 'Φόρτωση' },\n 'en-US': { loading: 'Loading' },\n 'es-ES': { loading: 'Cargando' },\n 'et-EE': { loading: 'Laadimine' },\n 'fi-FI': { loading: 'Ladataan' },\n 'fr-FR': { loading: 'Chargement' },\n 'he-IL': { loading: 'טוען' },\n 'hr-HR': { loading: 'Učitavanje' },\n 'hu-HU': { loading: 'Betöltés' },\n 'it-IT': { loading: 'Caricamento' },\n 'ja-JP': { loading: '読み込み中' },\n 'ko-KR': { loading: '로드 중' },\n 'lt-LT': { loading: 'Įkeliama' },\n 'lv-LV': { loading: 'Notiek ielāde' },\n 'nb-NO': { loading: 'Laster inn' },\n 'nl-NL': { loading: 'Laden' },\n 'pl-PL': { loading: 'Ładowanie' },\n 'pt-BR': { loading: 'Carregando' },\n 'pt-PT': { loading: 'A carregar' },\n 'ro-RO': { loading: 'Se încarcă' },\n 'ru-RU': { loading: 'Загрузка' },\n 'sk-SK': { loading: 'Načítava sa' },\n 'sl-SI': { loading: 'Nalaganje' },\n 'sr-SP': { loading: 'Učitavanje' },\n 'sv-SE': { loading: 'Läser in' },\n 'tr-TR': { loading: 'Yükleniyor' },\n 'uk-UA': { loading: 'Завантаження' },\n 'zh-CN': { loading: '正在加载' },\n 'zh-TW': { loading: '正在載入' },\n}\n\nexport { MESSAGES }\nexport type { LibraryMessages }\n"],"mappings":";AAgBA,MAAME,WAAsD;CAC1D,SAAS,EAAED,SAAS,eAAe;CACnC,SAAS,EAAEA,SAAS,YAAY;CAChC,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,eAAe;CACnC,SAAS,EAAEA,SAAS,UAAU;CAC9B,SAAS,EAAEA,SAAS,UAAU;CAC9B,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,YAAY;CAChC,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,OAAO;CAC3B,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,cAAc;CAClC,SAAS,EAAEA,SAAS,QAAQ;CAC5B,SAAS,EAAEA,SAAS,OAAO;CAC3B,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,gBAAgB;CACpC,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,QAAQ;CAC5B,SAAS,EAAEA,SAAS,YAAY;CAChC,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,cAAc;CAClC,SAAS,EAAEA,SAAS,YAAY;CAChC,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,WAAW;CAC/B,SAAS,EAAEA,SAAS,aAAa;CACjC,SAAS,EAAEA,SAAS,eAAe;CACnC,SAAS,EAAEA,SAAS,OAAO;CAC3B,SAAS,EAAEA,SAAS,OAAO;AAC7B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,16 +6,20 @@ import { Chip, ChipActionProps, ChipFilterProps, ChipProps, ChipVariant } from "
|
|
|
6
6
|
import { ChipGroup, ChipGroupChip, ChipGroupChipProps, ChipGroupProps } from "./components/chip-group/index.js";
|
|
7
7
|
import { CopyField, CopyFieldProps } from "./components/copy-field/index.js";
|
|
8
8
|
import { Currency, CurrencyProps, CurrencySignDisplay, CurrencyTone } from "./components/currency/index.js";
|
|
9
|
+
import { Dialog, DialogBody, DialogBodyProps, DialogContent, DialogContentProps, DialogFooter, DialogHeader, DialogPartProps, DialogProps, DialogTitle, DialogTitleProps } from "./components/dialog/index.js";
|
|
9
10
|
import { Form, FormHandle, FormProps } from "./components/form/index.js";
|
|
10
11
|
import { IconButton, IconButtonProps, IconButtonSize, IconButtonVariant } from "./components/icon-button/index.js";
|
|
11
12
|
import { Link, LinkProps, LinkTone, LinkUnderline } from "./components/link/index.js";
|
|
13
|
+
import { Meter, MeterProps, MeterTone } from "./components/meter/index.js";
|
|
12
14
|
import { FieldVariant } from "./field/index.js";
|
|
13
15
|
import { NumberField, NumberFieldProps } from "./components/number-field/index.js";
|
|
14
16
|
import { ProductIcon, ProductIconProps } from "./components/product-icon/index.js";
|
|
17
|
+
import { ProgressIndicator, ProgressIndicatorProps, ProgressIndicatorShape, ProgressIndicatorTone, ProgressIndicatorVariant } from "./components/progress-indicator/index.js";
|
|
15
18
|
import { Radio, RadioGroup, RadioGroupOrientation, RadioGroupProps, RadioProps } from "./components/radio-group/index.js";
|
|
16
19
|
import { SearchField, SearchFieldProps } from "./components/search-field/index.js";
|
|
17
20
|
import { SegmentedButton, SegmentedButtonProps, SegmentedButtonSegment, SegmentedButtonSegmentProps } from "./components/segmented-button/index.js";
|
|
18
21
|
import { Separator, SeparatorInset, SeparatorProps } from "./components/separator/index.js";
|
|
22
|
+
import { Snackbar, SnackbarAction, SnackbarOptions, SnackbarProps, SnackbarQueue } from "./components/snackbar/index.js";
|
|
19
23
|
import { Stack, StackAlign, StackGap, StackJustify, StackProps } from "./components/stack/index.js";
|
|
20
24
|
import { Switch, SwitchProps } from "./components/switch/index.js";
|
|
21
25
|
import { TypeRole } from "./typography/roles.js";
|
|
@@ -28,4 +32,4 @@ import { RouterProvider, RouterProviderProps } from "./router/index.js";
|
|
|
28
32
|
import { ThemeScope, ThemeScopeProps } from "./theme-scope.js";
|
|
29
33
|
import { ResolvedTheme, useTheme } from "./theme/scope.js";
|
|
30
34
|
import { Key, Selection } from "react-stately";
|
|
31
|
-
export { Avatar, type AvatarProps, Button, type ButtonProps, type ButtonShape, type ButtonSize, type ButtonVariant, Card, type CardProps, type CardVariant, Checkbox, type CheckboxProps, Chip, type ChipActionProps, type ChipFilterProps, ChipGroup, ChipGroupChip, type ChipGroupChipProps, type ChipGroupProps, type ChipProps, type ChipVariant, CopyField, type CopyFieldProps, Currency, type CurrencyProps, type CurrencySignDisplay, type CurrencyTone, type FieldVariant, Form, type FormHandle, type FormProps, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconStyle, type Key, type KuiTheme, Link, type LinkProps, type LinkTone, type LinkUnderline, NumberField, type NumberFieldProps, ProductIcon, type ProductIconProps, Radio, RadioGroup, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, type ResolvedTheme, RouterProvider, type RouterProviderProps, SearchField, type SearchFieldProps, SegmentedButton, type SegmentedButtonProps, SegmentedButtonSegment, type SegmentedButtonSegmentProps, type Selection, Separator, type SeparatorInset, type SeparatorProps, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Text, TextField, type TextFieldProps, type TextProps, ThemeScope, type ThemeScopeProps, type TypeRole, type WindowSizeClass, useIconStyle, useTheme, useWindowSizeClass };
|
|
35
|
+
export { Avatar, type AvatarProps, Button, type ButtonProps, type ButtonShape, type ButtonSize, type ButtonVariant, Card, type CardProps, type CardVariant, Checkbox, type CheckboxProps, Chip, type ChipActionProps, type ChipFilterProps, ChipGroup, ChipGroupChip, type ChipGroupChipProps, type ChipGroupProps, type ChipProps, type ChipVariant, CopyField, type CopyFieldProps, Currency, type CurrencyProps, type CurrencySignDisplay, type CurrencyTone, Dialog, DialogBody, type DialogBodyProps, DialogContent, type DialogContentProps, DialogFooter, DialogHeader, type DialogPartProps, type DialogProps, DialogTitle, type DialogTitleProps, type FieldVariant, Form, type FormHandle, type FormProps, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, type IconStyle, type Key, type KuiTheme, Link, type LinkProps, type LinkTone, type LinkUnderline, Meter, type MeterProps, type MeterTone, NumberField, type NumberFieldProps, ProductIcon, type ProductIconProps, ProgressIndicator, type ProgressIndicatorProps, type ProgressIndicatorShape, type ProgressIndicatorTone, type ProgressIndicatorVariant, Radio, RadioGroup, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, type ResolvedTheme, RouterProvider, type RouterProviderProps, SearchField, type SearchFieldProps, SegmentedButton, type SegmentedButtonProps, SegmentedButtonSegment, type SegmentedButtonSegmentProps, type Selection, Separator, type SeparatorInset, type SeparatorProps, Snackbar, type SnackbarAction, type SnackbarOptions, type SnackbarProps, type SnackbarQueue, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Text, TextField, type TextFieldProps, type TextProps, ThemeScope, type ThemeScopeProps, type TypeRole, type WindowSizeClass, useIconStyle, useTheme, useWindowSizeClass };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { useIconStyle } from "./glyphs/icon-style.js";
|
|
|
2
2
|
import { useTheme } from "./theme/scope.js";
|
|
3
3
|
import { Avatar } from "./components/avatar/index.js";
|
|
4
4
|
import { RouterProvider } from "./router/index.js";
|
|
5
|
+
import { Text } from "./components/text/index.js";
|
|
6
|
+
import { ProgressIndicator } from "./components/progress-indicator/index.js";
|
|
5
7
|
import { Button } from "./components/button/index.js";
|
|
6
8
|
import { Card } from "./components/card/index.js";
|
|
7
9
|
import { Checkbox } from "./components/checkbox/index.js";
|
|
@@ -9,19 +11,21 @@ import { Chip } from "./components/chip/index.js";
|
|
|
9
11
|
import { ChipGroup, ChipGroupChip } from "./components/chip-group/index.js";
|
|
10
12
|
import { CopyField } from "./components/copy-field/index.js";
|
|
11
13
|
import { Currency } from "./components/currency/index.js";
|
|
14
|
+
import { useWindowSizeClass } from "./hooks/useWindowSizeClass.js";
|
|
15
|
+
import { Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "./components/dialog/index.js";
|
|
12
16
|
import { Form } from "./components/form/index.js";
|
|
13
17
|
import { IconButton } from "./components/icon-button/index.js";
|
|
14
18
|
import { Link } from "./components/link/index.js";
|
|
19
|
+
import { Meter } from "./components/meter/index.js";
|
|
15
20
|
import { NumberField } from "./components/number-field/index.js";
|
|
16
21
|
import { ProductIcon } from "./components/product-icon/index.js";
|
|
17
22
|
import { Radio, RadioGroup } from "./components/radio-group/index.js";
|
|
18
23
|
import { SearchField } from "./components/search-field/index.js";
|
|
19
24
|
import { SegmentedButton, SegmentedButtonSegment } from "./components/segmented-button/index.js";
|
|
20
25
|
import { Separator } from "./components/separator/index.js";
|
|
26
|
+
import { Snackbar } from "./components/snackbar/index.js";
|
|
21
27
|
import { Stack } from "./components/stack/index.js";
|
|
22
28
|
import { Switch } from "./components/switch/index.js";
|
|
23
|
-
import { Text } from "./components/text/index.js";
|
|
24
29
|
import { TextField } from "./components/text-field/index.js";
|
|
25
|
-
import { useWindowSizeClass } from "./hooks/useWindowSizeClass.js";
|
|
26
30
|
import { ThemeScope } from "./theme-scope.js";
|
|
27
|
-
export { Avatar, Button, Card, Checkbox, Chip, ChipGroup, ChipGroupChip, CopyField, Currency, Form, IconButton, Link, NumberField, ProductIcon, Radio, RadioGroup, RouterProvider, SearchField, SegmentedButton, SegmentedButtonSegment, Separator, Stack, Switch, Text, TextField, ThemeScope, useIconStyle, useTheme, useWindowSizeClass };
|
|
31
|
+
export { Avatar, Button, Card, Checkbox, Chip, ChipGroup, ChipGroupChip, CopyField, Currency, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Form, IconButton, Link, Meter, NumberField, ProductIcon, ProgressIndicator, Radio, RadioGroup, RouterProvider, SearchField, SegmentedButton, SegmentedButtonSegment, Separator, Snackbar, Stack, Switch, Text, TextField, ThemeScope, useIconStyle, useTheme, useWindowSizeClass };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "react-native";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import "react-native-reanimated";
|
|
4
|
+
//#region src/indicator/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The line's colour. `primary` is the page's own pair. `negative` and
|
|
7
|
+
* `positive` swap the active indicator and the stop indicator alone, leaving
|
|
8
|
+
* the track in secondary container, so a column of meters reads as one scale
|
|
9
|
+
* with bars of different colours. `inherit` takes the colour of the slot the
|
|
10
|
+
* line sits in for both, and gives the track a quarter of it.
|
|
11
|
+
*/
|
|
12
|
+
type IndicatorTone = 'inherit' | 'negative' | 'positive' | 'primary';
|
|
13
|
+
//#endregion
|
|
14
|
+
export type { IndicatorTone };
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { useIconStyle } from "../glyphs/icon-style.js";
|
|
2
|
+
import { useTheme } from "../theme/scope.js";
|
|
3
|
+
import { composeStyles } from "../theme/styles.js";
|
|
4
|
+
import { withOpacity } from "../theme/color.js";
|
|
5
|
+
import { Text as Text$1 } from "../components/text/index.js";
|
|
6
|
+
import { localeOf } from "../number/locale.js";
|
|
7
|
+
import { styles, stylesOf } from "./styles.js";
|
|
8
|
+
import { c } from "react/compiler-runtime";
|
|
9
|
+
import { View } from "react-native";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
|
|
13
|
+
import { NumberFormatter } from "@internationalized/number";
|
|
14
|
+
//#region src/indicator/index.tsx
|
|
15
|
+
/**
|
|
16
|
+
* The active indicator's length at a value: the value's share of what the two
|
|
17
|
+
* gaps and the stop leave of the row, as web's `activeAt` gives it. A share of
|
|
18
|
+
* the whole row would run the row 12dp long at the maximum, with the stop
|
|
19
|
+
* drawn past its end.
|
|
20
|
+
*/
|
|
21
|
+
function activeLength(width, percentage) {
|
|
22
|
+
"worklet";
|
|
23
|
+
return Math.max(0, width - 12) * percentage / 100;
|
|
24
|
+
}
|
|
25
|
+
function fillOf(color) {
|
|
26
|
+
return { backgroundColor: color };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The label and, optionally, the value, on a row above the indicator, both in
|
|
30
|
+
* the label-large role and on-surface-variant. Renders nothing when there is
|
|
31
|
+
* neither to show.
|
|
32
|
+
*/
|
|
33
|
+
function IndicatorLabels(t0) {
|
|
34
|
+
const $ = c(12);
|
|
35
|
+
const { label, value } = t0;
|
|
36
|
+
const theme = useTheme();
|
|
37
|
+
const hasLabel = label !== void 0 && label !== "";
|
|
38
|
+
const hasValue = value !== void 0 && value !== "";
|
|
39
|
+
if (!hasLabel && !hasValue) return null;
|
|
40
|
+
let t1;
|
|
41
|
+
if ($[0] !== theme) {
|
|
42
|
+
t1 = stylesOf(theme);
|
|
43
|
+
$[0] = theme;
|
|
44
|
+
$[1] = t1;
|
|
45
|
+
} else t1 = $[1];
|
|
46
|
+
let t2;
|
|
47
|
+
if ($[2] !== hasLabel || $[3] !== label) {
|
|
48
|
+
t2 = hasLabel ? /* @__PURE__ */ jsx(Text$1, {
|
|
49
|
+
style: styles.label,
|
|
50
|
+
tone: "muted",
|
|
51
|
+
variant: "labelLarge",
|
|
52
|
+
children: label
|
|
53
|
+
}) : null;
|
|
54
|
+
$[2] = hasLabel;
|
|
55
|
+
$[3] = label;
|
|
56
|
+
$[4] = t2;
|
|
57
|
+
} else t2 = $[4];
|
|
58
|
+
let t3;
|
|
59
|
+
if ($[5] !== hasValue || $[6] !== value) {
|
|
60
|
+
t3 = hasValue ? /* @__PURE__ */ jsx(Text$1, {
|
|
61
|
+
numberOfLines: 1,
|
|
62
|
+
style: styles.value,
|
|
63
|
+
tone: "muted",
|
|
64
|
+
variant: "labelLarge",
|
|
65
|
+
children: value
|
|
66
|
+
}) : null;
|
|
67
|
+
$[5] = hasValue;
|
|
68
|
+
$[6] = value;
|
|
69
|
+
$[7] = t3;
|
|
70
|
+
} else t3 = $[7];
|
|
71
|
+
let t4;
|
|
72
|
+
if ($[8] !== t1.labels || $[9] !== t2 || $[10] !== t3) {
|
|
73
|
+
t4 = /* @__PURE__ */ jsxs(View, {
|
|
74
|
+
style: t1.labels,
|
|
75
|
+
children: [t2, t3]
|
|
76
|
+
});
|
|
77
|
+
$[8] = t1.labels;
|
|
78
|
+
$[9] = t2;
|
|
79
|
+
$[10] = t3;
|
|
80
|
+
$[11] = t4;
|
|
81
|
+
} else t4 = $[11];
|
|
82
|
+
return t4;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The page's line: the active indicator at its share of the measured width,
|
|
86
|
+
* the track taking what is left, and the stop indicator at the end. Anything
|
|
87
|
+
* passed as `children` is drawn inside the track, which is what the progress
|
|
88
|
+
* indicator's buffer fills it with; with none the track is solid.
|
|
89
|
+
* `activeOpacity` is how far the active indicator shows, which is how the
|
|
90
|
+
* progress indicator fades it under a wave.
|
|
91
|
+
*/
|
|
92
|
+
function IndicatorLine(t0) {
|
|
93
|
+
const $ = c(3);
|
|
94
|
+
const { activeOpacity, children, colors, percentage, width } = t0;
|
|
95
|
+
const theme = useTheme();
|
|
96
|
+
const sheet = stylesOf(theme);
|
|
97
|
+
const active = useAnimatedStyle(() => {
|
|
98
|
+
"worklet";
|
|
99
|
+
return {
|
|
100
|
+
opacity: activeOpacity === void 0 ? 1 : activeOpacity.get(),
|
|
101
|
+
width: activeLength(width, percentage.get())
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const T0 = View;
|
|
105
|
+
const t1 = composeStyles(styles.stop, sheet.stop, fillOf(colors.active));
|
|
106
|
+
let t2;
|
|
107
|
+
if ($[0] !== T0 || $[1] !== t1) {
|
|
108
|
+
t2 = /* @__PURE__ */ jsx(T0, { style: t1 });
|
|
109
|
+
$[0] = T0;
|
|
110
|
+
$[1] = t1;
|
|
111
|
+
$[2] = t2;
|
|
112
|
+
} else t2 = $[2];
|
|
113
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
114
|
+
style: styles.line,
|
|
115
|
+
children: [
|
|
116
|
+
/* @__PURE__ */ jsx(Animated.View, { style: composeStyles(styles.active, sheet.active, fillOf(colors.active), active) }),
|
|
117
|
+
/* @__PURE__ */ jsx(View, {
|
|
118
|
+
style: composeStyles(styles.track, sheet.track, children === void 0 ? fillOf(colors.track) : void 0),
|
|
119
|
+
children
|
|
120
|
+
}),
|
|
121
|
+
t2
|
|
122
|
+
]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The value as React Aria's `useProgressBar` and `useMeter` read it: clamped to the range,
|
|
127
|
+
* as a percentage of it, and written out by `formatOptions` in the locale,
|
|
128
|
+
* the share of the range under a percent style and the value itself under
|
|
129
|
+
* any other, unless `valueLabel` says it in words.
|
|
130
|
+
*/
|
|
131
|
+
function readingOf({ formatOptions = { style: "percent" }, locale, maxValue, minValue, value, valueLabel }) {
|
|
132
|
+
const clamped = Math.min(Math.max(value, minValue), maxValue);
|
|
133
|
+
const range = maxValue - minValue;
|
|
134
|
+
const share = range === 0 ? 0 : (clamped - minValue) / range;
|
|
135
|
+
const text = valueLabel ?? new NumberFormatter(localeOf(locale), formatOptions).format(formatOptions.style === "percent" ? share : clamped);
|
|
136
|
+
return {
|
|
137
|
+
percentage: share * 100,
|
|
138
|
+
text,
|
|
139
|
+
value: clamped
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* A value that moves to each new target over `timing`, or jumps to it while
|
|
144
|
+
* the reader has asked for reduced motion, as web's transition drops to `0s`
|
|
145
|
+
* there. It starts at its first target, so nothing moves as it mounts.
|
|
146
|
+
*/
|
|
147
|
+
function useEased(target, timing) {
|
|
148
|
+
const $ = c(10);
|
|
149
|
+
const reduceMotion = useReducedMotion();
|
|
150
|
+
const shown = useSharedValue(target);
|
|
151
|
+
const { duration } = timing;
|
|
152
|
+
const [x1, y1, x2, y2] = timing.easing;
|
|
153
|
+
let t0;
|
|
154
|
+
let t1;
|
|
155
|
+
if ($[0] !== duration || $[1] !== reduceMotion || $[2] !== shown || $[3] !== target || $[4] !== x1 || $[5] !== x2 || $[6] !== y1 || $[7] !== y2) {
|
|
156
|
+
t0 = () => {
|
|
157
|
+
shown.set(reduceMotion ? target : withTiming(target, {
|
|
158
|
+
duration,
|
|
159
|
+
easing: Easing.bezier(x1, y1, x2, y2)
|
|
160
|
+
}));
|
|
161
|
+
};
|
|
162
|
+
t1 = [
|
|
163
|
+
duration,
|
|
164
|
+
reduceMotion,
|
|
165
|
+
shown,
|
|
166
|
+
target,
|
|
167
|
+
x1,
|
|
168
|
+
x2,
|
|
169
|
+
y1,
|
|
170
|
+
y2
|
|
171
|
+
];
|
|
172
|
+
$[0] = duration;
|
|
173
|
+
$[1] = reduceMotion;
|
|
174
|
+
$[2] = shown;
|
|
175
|
+
$[3] = target;
|
|
176
|
+
$[4] = x1;
|
|
177
|
+
$[5] = x2;
|
|
178
|
+
$[6] = y1;
|
|
179
|
+
$[7] = y2;
|
|
180
|
+
$[8] = t0;
|
|
181
|
+
$[9] = t1;
|
|
182
|
+
} else {
|
|
183
|
+
t0 = $[8];
|
|
184
|
+
t1 = $[9];
|
|
185
|
+
}
|
|
186
|
+
useEffect(t0, t1);
|
|
187
|
+
return shown;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The colours a tone draws the line in. `inherit` reads the colour of the
|
|
191
|
+
* slot the line sits in, as a button hands its content colour to what it
|
|
192
|
+
* holds, and falls back to on-surface outside one.
|
|
193
|
+
*/
|
|
194
|
+
function useIndicatorColors(tone) {
|
|
195
|
+
const $ = c(8);
|
|
196
|
+
const { colors } = useTheme();
|
|
197
|
+
const slot = useIconStyle().color;
|
|
198
|
+
if (tone === "inherit") {
|
|
199
|
+
const color = typeof slot === "string" ? slot : colors.onSurface;
|
|
200
|
+
let t0;
|
|
201
|
+
if ($[0] !== color) {
|
|
202
|
+
t0 = withOpacity(color, .25);
|
|
203
|
+
$[0] = color;
|
|
204
|
+
$[1] = t0;
|
|
205
|
+
} else t0 = $[1];
|
|
206
|
+
let t1;
|
|
207
|
+
if ($[2] !== color || $[3] !== t0) {
|
|
208
|
+
t1 = {
|
|
209
|
+
active: color,
|
|
210
|
+
track: t0
|
|
211
|
+
};
|
|
212
|
+
$[2] = color;
|
|
213
|
+
$[3] = t0;
|
|
214
|
+
$[4] = t1;
|
|
215
|
+
} else t1 = $[4];
|
|
216
|
+
return t1;
|
|
217
|
+
}
|
|
218
|
+
const t0 = colors[tone];
|
|
219
|
+
let t1;
|
|
220
|
+
if ($[5] !== colors.secondaryContainer || $[6] !== t0) {
|
|
221
|
+
t1 = {
|
|
222
|
+
active: t0,
|
|
223
|
+
track: colors.secondaryContainer
|
|
224
|
+
};
|
|
225
|
+
$[5] = colors.secondaryContainer;
|
|
226
|
+
$[6] = t0;
|
|
227
|
+
$[7] = t1;
|
|
228
|
+
} else t1 = $[7];
|
|
229
|
+
return t1;
|
|
230
|
+
}
|
|
231
|
+
/** The width a view was last laid out at, and the handler that reads it. */
|
|
232
|
+
function useMeasuredWidth() {
|
|
233
|
+
const $ = c(3);
|
|
234
|
+
const [width, setWidth] = useState(0);
|
|
235
|
+
let t0;
|
|
236
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
237
|
+
t0 = (event) => {
|
|
238
|
+
setWidth(event.nativeEvent.layout.width);
|
|
239
|
+
};
|
|
240
|
+
$[0] = t0;
|
|
241
|
+
} else t0 = $[0];
|
|
242
|
+
let t1;
|
|
243
|
+
if ($[1] !== width) {
|
|
244
|
+
t1 = {
|
|
245
|
+
onLayout: t0,
|
|
246
|
+
width
|
|
247
|
+
};
|
|
248
|
+
$[1] = width;
|
|
249
|
+
$[2] = t1;
|
|
250
|
+
} else t1 = $[2];
|
|
251
|
+
return t1;
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
export { IndicatorLabels, IndicatorLine, activeLength, fillOf, readingOf, useEased, useIndicatorColors, useMeasuredWidth };
|
|
255
|
+
|
|
256
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["ReactNode","LayoutChangeEvent","ViewStyle","AnimatedStyle","SharedValue","NumberFormatter","useEffect","useState","View","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","Text","useIconStyle","localeOf","withOpacity","useTheme","composeStyles","RESERVED","styles","stylesOf","IndicatorColors","active","track","IndicatorTiming","duration","easing","IndicatorTone","activeLength","width","percentage","Math","max","fillOf","color","backgroundColor","IndicatorLabels","t0","$","_c","label","value","theme","hasLabel","undefined","hasValue","t1","t2","t3","t4","labels","IndicatorLine","activeOpacity","children","colors","sheet","opacity","get","T0","stop","line","readingOf","formatOptions","style","locale","maxValue","minValue","valueLabel","Intl","NumberFormatOptions","clamped","min","range","share","text","format","useEased","target","timing","reduceMotion","shown","x1","y1","x2","y2","set","bezier","useIndicatorColors","tone","slot","onSurface","secondaryContainer","useMeasuredWidth","setWidth","Symbol","for","event","nativeEvent","layout","onLayout","_c","Text"],"sources":["../../src/indicator/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { LayoutChangeEvent, ViewStyle } from 'react-native'\nimport type { AnimatedStyle, SharedValue } from 'react-native-reanimated'\n\nimport { NumberFormatter } from '@internationalized/number'\nimport { useEffect, useState } from 'react'\nimport { View } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport { Text } from '../components/text'\nimport { useIconStyle } from '../glyphs/icon-style'\nimport { localeOf } from '../number/locale'\nimport { withOpacity } from '../theme/color'\nimport { useTheme } from '../theme/scope'\nimport { composeStyles } from '../theme/styles'\nimport { RESERVED, styles, stylesOf } from './styles'\n\n// The pieces a linear indicator is drawn from, shared by ProgressIndicator and\n// Meter as web's src/indicator is. The styles they use are in ./styles.ts.\n//\n// Web lays the line out with `calc()` against the row's own width. React\n// Native has no `calc()`, so the row is measured through `onLayout` and the\n// active indicator's width worked out from that, which is also the length a\n// wave along the line follows.\n\n/** What a line draws its parts in. */\ntype IndicatorColors = {\n /** The active indicator and the stop indicator. */\n readonly active: string\n /** The track. */\n readonly track: string\n}\n\n/** How a value that changes moves: a duration in ms and a cubic Bézier. */\ntype IndicatorTiming = {\n readonly duration: number\n readonly easing: readonly [number, number, number, number]\n}\n\n/**\n * The line's colour. `primary` is the page's own pair. `negative` and\n * `positive` swap the active indicator and the stop indicator alone, leaving\n * the track in secondary container, so a column of meters reads as one scale\n * with bars of different colours. `inherit` takes the colour of the slot the\n * line sits in for both, and gives the track a quarter of it.\n */\ntype IndicatorTone = 'inherit' | 'negative' | 'positive' | 'primary'\n\n/**\n * The active indicator's length at a value: the value's share of what the two\n * gaps and the stop leave of the row, as web's `activeAt` gives it. A share of\n * the whole row would run the row 12dp long at the maximum, with the stop\n * drawn past its end.\n */\nfunction activeLength(width: number, percentage: number) {\n 'worklet'\n return (Math.max(0, width - RESERVED) * percentage) / 100\n}\n\n// A view's fill, built by a call rather than written inline at the prop,\n// which is what react-perf's no-new-object-as-prop is after.\nfunction fillOf(color: string) {\n return { backgroundColor: color }\n}\n\n/**\n * The label and, optionally, the value, on a row above the indicator, both in\n * the label-large role and on-surface-variant. Renders nothing when there is\n * neither to show.\n */\nfunction IndicatorLabels({\n label,\n value,\n}: {\n readonly label: string | undefined\n readonly value: string | undefined\n}) {\n const theme = useTheme()\n const hasLabel = label !== undefined && label !== ''\n const hasValue = value !== undefined && value !== ''\n\n if (!hasLabel && !hasValue) {\n return null\n }\n\n return (\n <View style={stylesOf(theme).labels}>\n {hasLabel ? (\n <Text style={styles.label} tone=\"muted\" variant=\"labelLarge\">\n {label}\n </Text>\n ) : null}\n {hasValue ? (\n <Text\n numberOfLines={1}\n style={styles.value}\n tone=\"muted\"\n variant=\"labelLarge\"\n >\n {value}\n </Text>\n ) : null}\n </View>\n )\n}\n\n/**\n * The page's line: the active indicator at its share of the measured width,\n * the track taking what is left, and the stop indicator at the end. Anything\n * passed as `children` is drawn inside the track, which is what the progress\n * indicator's buffer fills it with; with none the track is solid.\n * `activeOpacity` is how far the active indicator shows, which is how the\n * progress indicator fades it under a wave.\n */\nfunction IndicatorLine({\n activeOpacity,\n children,\n colors,\n percentage,\n width,\n}: {\n readonly activeOpacity?: SharedValue<number>\n readonly children?: ReactNode\n readonly colors: IndicatorColors\n readonly percentage: SharedValue<number>\n readonly width: number\n}) {\n const theme = useTheme()\n const sheet = stylesOf(theme)\n const active = useAnimatedStyle(() => {\n 'worklet'\n return {\n opacity: activeOpacity === undefined ? 1 : activeOpacity.get(),\n width: activeLength(width, percentage.get()),\n }\n })\n\n return (\n <View style={styles.line}>\n <Animated.View\n style={composeStyles<AnimatedStyle<ViewStyle>>(\n styles.active,\n sheet.active,\n fillOf(colors.active),\n active,\n )}\n />\n <View\n style={composeStyles<ViewStyle>(\n styles.track,\n sheet.track,\n children === undefined ? fillOf(colors.track) : undefined,\n )}\n >\n {children}\n </View>\n <View\n style={composeStyles<ViewStyle>(\n styles.stop,\n sheet.stop,\n fillOf(colors.active),\n )}\n />\n </View>\n )\n}\n\n/**\n * The value as React Aria's `useProgressBar` and `useMeter` read it: clamped to the range,\n * as a percentage of it, and written out by `formatOptions` in the locale,\n * the share of the range under a percent style and the value itself under\n * any other, unless `valueLabel` says it in words.\n */\nfunction readingOf({\n formatOptions = { style: 'percent' },\n locale,\n maxValue,\n minValue,\n value,\n valueLabel,\n}: {\n readonly formatOptions: Intl.NumberFormatOptions | undefined\n readonly locale: string | undefined\n readonly maxValue: number\n readonly minValue: number\n readonly value: number\n readonly valueLabel: string | undefined\n}) {\n const clamped = Math.min(Math.max(value, minValue), maxValue)\n const range = maxValue - minValue\n const share = range === 0 ? 0 : (clamped - minValue) / range\n const text =\n valueLabel ??\n new NumberFormatter(localeOf(locale), formatOptions).format(\n formatOptions.style === 'percent' ? share : clamped,\n )\n\n return { percentage: share * 100, text, value: clamped }\n}\n\n/**\n * A value that moves to each new target over `timing`, or jumps to it while\n * the reader has asked for reduced motion, as web's transition drops to `0s`\n * there. It starts at its first target, so nothing moves as it mounts.\n */\nfunction useEased(target: number, timing: IndicatorTiming) {\n const reduceMotion = useReducedMotion()\n const shown = useSharedValue(target)\n const { duration } = timing\n const [x1, y1, x2, y2] = timing.easing\n\n useEffect(() => {\n shown.set(\n reduceMotion\n ? target\n : withTiming(target, {\n duration,\n easing: Easing.bezier(x1, y1, x2, y2),\n }),\n )\n }, [duration, reduceMotion, shown, target, x1, x2, y1, y2])\n\n return shown\n}\n\n/**\n * The colours a tone draws the line in. `inherit` reads the colour of the\n * slot the line sits in, as a button hands its content colour to what it\n * holds, and falls back to on-surface outside one.\n */\nfunction useIndicatorColors(tone: IndicatorTone): IndicatorColors {\n const { colors } = useTheme()\n const slot = useIconStyle().color\n\n if (tone === 'inherit') {\n const color = typeof slot === 'string' ? slot : colors.onSurface\n\n return { active: color, track: withOpacity(color, 0.25) }\n }\n\n return { active: colors[tone], track: colors.secondaryContainer }\n}\n\n/** The width a view was last laid out at, and the handler that reads it. */\nfunction useMeasuredWidth() {\n const [width, setWidth] = useState(0)\n\n return {\n onLayout: (event: LayoutChangeEvent) => {\n setWidth(event.nativeEvent.layout.width)\n },\n width,\n }\n}\n\nexport {\n activeLength,\n fillOf,\n IndicatorLabels,\n IndicatorLine,\n readingOf,\n useEased,\n useIndicatorColors,\n useMeasuredWidth,\n}\nexport type { IndicatorColors, IndicatorTiming, IndicatorTone }\n"],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { c } from "react/compiler-runtime";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { Easing, ReduceMotion, cancelAnimation, useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
|
|
4
|
+
//#region src/indicator/motion.ts
|
|
5
|
+
const SEARCH_STEPS = 24;
|
|
6
|
+
/**
|
|
7
|
+
* The value `frames` give at `time`, a share of the loop: the frames either
|
|
8
|
+
* side of it, and the curve between them.
|
|
9
|
+
*
|
|
10
|
+
* A curve is a function of its parameter rather than of the time, so the
|
|
11
|
+
* parameter is found by halving the interval until the curve's own `x` is the
|
|
12
|
+
* share of the segment gone, which holds for every curve whose control points
|
|
13
|
+
* lie inside the unit interval across, as every timing function's do. The
|
|
14
|
+
* search is nested here rather than beside it: a worklet captures what it
|
|
15
|
+
* calls when the module is evaluated, so one calling another declared below it
|
|
16
|
+
* would be handed nothing, and the linter orders declarations by name.
|
|
17
|
+
*/
|
|
18
|
+
function keyframeAt(frames, time) {
|
|
19
|
+
"worklet";
|
|
20
|
+
let index = 0;
|
|
21
|
+
while (index < frames.length - 2 && time >= frames[index + 1][0]) index++;
|
|
22
|
+
const [from, start, curve] = frames[index];
|
|
23
|
+
const [to, end] = frames[index + 1];
|
|
24
|
+
const share = Math.min(1, Math.max(0, (time - from) / (to - from)));
|
|
25
|
+
if (curve === void 0 || share === 0 || share === 1) return start + (end - start) * share;
|
|
26
|
+
function coordinateAt(first, second, parameter) {
|
|
27
|
+
const rest = 1 - parameter;
|
|
28
|
+
return 3 * rest * rest * parameter * first + 3 * rest * parameter * parameter * second + parameter * parameter * parameter;
|
|
29
|
+
}
|
|
30
|
+
const [x1, y1, x2, y2] = curve;
|
|
31
|
+
let low = 0;
|
|
32
|
+
let high = 1;
|
|
33
|
+
let parameter = share;
|
|
34
|
+
for (let step = 0; step < SEARCH_STEPS; step++) {
|
|
35
|
+
if (coordinateAt(x1, x2, parameter) < share) low = parameter;
|
|
36
|
+
else high = parameter;
|
|
37
|
+
parameter = (low + high) / 2;
|
|
38
|
+
}
|
|
39
|
+
return start + (end - start) * coordinateAt(y1, y2, parameter);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A clock running from 0 to 1 over `duration` ms and starting again, for as
|
|
43
|
+
* long as `running` holds. It runs whatever the device says about reduced
|
|
44
|
+
* motion: Reanimated would otherwise jump a looping animation to its end,
|
|
45
|
+
* which freezes an indeterminate indicator, so a component slows its loop
|
|
46
|
+
* there by lengthening `duration` instead, as web's does.
|
|
47
|
+
*/
|
|
48
|
+
function useLoop(duration, running) {
|
|
49
|
+
const $ = c(5);
|
|
50
|
+
const clock = useSharedValue(0);
|
|
51
|
+
let t0;
|
|
52
|
+
let t1;
|
|
53
|
+
if ($[0] !== clock || $[1] !== duration || $[2] !== running) {
|
|
54
|
+
t0 = () => {
|
|
55
|
+
if (!running) return;
|
|
56
|
+
clock.set(0);
|
|
57
|
+
clock.set(withRepeat(withTiming(1, {
|
|
58
|
+
duration,
|
|
59
|
+
easing: Easing.linear,
|
|
60
|
+
reduceMotion: ReduceMotion.Never
|
|
61
|
+
}), -1, false, void 0, ReduceMotion.Never));
|
|
62
|
+
return () => {
|
|
63
|
+
cancelAnimation(clock);
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
t1 = [
|
|
67
|
+
clock,
|
|
68
|
+
duration,
|
|
69
|
+
running
|
|
70
|
+
];
|
|
71
|
+
$[0] = clock;
|
|
72
|
+
$[1] = duration;
|
|
73
|
+
$[2] = running;
|
|
74
|
+
$[3] = t0;
|
|
75
|
+
$[4] = t1;
|
|
76
|
+
} else {
|
|
77
|
+
t0 = $[3];
|
|
78
|
+
t1 = $[4];
|
|
79
|
+
}
|
|
80
|
+
useEffect(t0, t1);
|
|
81
|
+
return clock;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { keyframeAt, useLoop };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.js","names":["SharedValue","useEffect","cancelAnimation","Easing","ReduceMotion","useSharedValue","withRepeat","withTiming","Curve","Keyframe","at","value","curve","SEARCH_STEPS","keyframeAt","frames","time","index","length","from","start","to","end","share","Math","min","max","undefined","coordinateAt","first","second","parameter","rest","x1","y1","x2","y2","low","high","step","useLoop","duration","running","$","_c","clock","t0","t1","set","easing","linear","reduceMotion","Never","_c"],"sources":["../../src/indicator/motion.ts"],"sourcesContent":["import type { SharedValue } from 'react-native-reanimated'\n\nimport { useEffect } from 'react'\nimport {\n cancelAnimation,\n Easing,\n ReduceMotion,\n useSharedValue,\n withRepeat,\n withTiming,\n} from 'react-native-reanimated'\n\n// The machinery an indeterminate indicator's choreography runs on: a clock\n// that loops from 0 to 1, and the keyframes read off it. Web writes the same\n// choreography as CSS keyframes, each segment on a curve of its own; React\n// Native has no keyframes, so each property here is a list of the same\n// frames, read at the clock's time on the UI thread.\n\n/** A cubic Bézier's four control coordinates, in the order CSS writes them. */\ntype Curve = readonly [number, number, number, number]\n\n/**\n * One keyframe: where it falls in the loop, from 0 to 1, the value there,\n * and the curve the value takes to the next frame. A frame with no curve\n * moves to the next one at a constant rate, as a CSS keyframe with no timing\n * function does under a linear animation.\n */\ntype Keyframe = readonly [at: number, value: number, curve?: Curve]\n\n// How finely a curve's progress is searched for. Twenty-four halvings leave\n// it a sixteen-millionth out, far finer than a frame can show.\nconst SEARCH_STEPS = 24\n\n/**\n * The value `frames` give at `time`, a share of the loop: the frames either\n * side of it, and the curve between them.\n *\n * A curve is a function of its parameter rather than of the time, so the\n * parameter is found by halving the interval until the curve's own `x` is the\n * share of the segment gone, which holds for every curve whose control points\n * lie inside the unit interval across, as every timing function's do. The\n * search is nested here rather than beside it: a worklet captures what it\n * calls when the module is evaluated, so one calling another declared below it\n * would be handed nothing, and the linter orders declarations by name.\n */\nfunction keyframeAt(frames: readonly Keyframe[], time: number) {\n 'worklet'\n let index = 0\n while (index < frames.length - 2 && time >= frames[index + 1][0]) {\n index++\n }\n\n const [from, start, curve] = frames[index]\n const [to, end] = frames[index + 1]\n const share = Math.min(1, Math.max(0, (time - from) / (to - from)))\n\n if (curve === undefined || share === 0 || share === 1) {\n return start + (end - start) * share\n }\n\n // One coordinate of the curve from (0, 0) to (1, 1) at a parameter, given\n // the same coordinate of its two control points.\n // oxlint-disable-next-line unicorn/consistent-function-scoping -- a worklet captures what it calls when the module is evaluated; see above\n function coordinateAt(first: number, second: number, parameter: number) {\n const rest = 1 - parameter\n return (\n 3 * rest * rest * parameter * first +\n 3 * rest * parameter * parameter * second +\n parameter * parameter * parameter\n )\n }\n\n const [x1, y1, x2, y2] = curve\n let low = 0\n let high = 1\n let parameter = share\n for (let step = 0; step < SEARCH_STEPS; step++) {\n if (coordinateAt(x1, x2, parameter) < share) {\n low = parameter\n } else {\n high = parameter\n }\n parameter = (low + high) / 2\n }\n\n return start + (end - start) * coordinateAt(y1, y2, parameter)\n}\n\n/**\n * A clock running from 0 to 1 over `duration` ms and starting again, for as\n * long as `running` holds. It runs whatever the device says about reduced\n * motion: Reanimated would otherwise jump a looping animation to its end,\n * which freezes an indeterminate indicator, so a component slows its loop\n * there by lengthening `duration` instead, as web's does.\n */\nfunction useLoop(duration: number, running: boolean): SharedValue<number> {\n const clock = useSharedValue(0)\n\n useEffect(() => {\n if (!running) {\n return undefined\n }\n\n clock.set(0)\n clock.set(\n withRepeat(\n withTiming(1, {\n duration,\n easing: Easing.linear,\n reduceMotion: ReduceMotion.Never,\n }),\n -1,\n false,\n undefined,\n ReduceMotion.Never,\n ),\n )\n\n return () => {\n cancelAnimation(clock)\n }\n }, [clock, duration, running])\n\n return clock\n}\n\nexport { keyframeAt, useLoop }\nexport type { Curve, Keyframe }\n"],"mappings":";;;;AA+BA,MAAMa,eAAe;;;;;;;;;;;;;AAcrB,SAASC,WAAWC,QAA6BC,MAAc;CAC7D;CACA,IAAIC,QAAQ;CACZ,OAAOA,QAAQF,OAAOG,SAAS,KAAKF,QAAQD,OAAOE,QAAQ,EAAE,CAAC,IAC5DA;CAGF,MAAM,CAACE,MAAMC,OAAOR,SAASG,OAAOE;CACpC,MAAM,CAACI,IAAIC,OAAOP,OAAOE,QAAQ;CACjC,MAAMM,QAAQC,KAAKC,IAAI,GAAGD,KAAKE,IAAI,IAAIV,OAAOG,SAASE,KAAKF,KAAK,CAAC;CAElE,IAAIP,UAAUe,KAAAA,KAAaJ,UAAU,KAAKA,UAAU,GAClD,OAAOH,SAASE,MAAMF,SAASG;CAMjC,SAASK,aAAaC,OAAeC,QAAgBC,WAAmB;EACtE,MAAMC,OAAO,IAAID;EACjB,OACE,IAAIC,OAAOA,OAAOD,YAAYF,QAC9B,IAAIG,OAAOD,YAAYA,YAAYD,SACnCC,YAAYA,YAAYA;CAE5B;CAEA,MAAM,CAACE,IAAIC,IAAIC,IAAIC,MAAMxB;CACzB,IAAIyB,MAAM;CACV,IAAIC,OAAO;CACX,IAAIP,YAAYR;CAChB,KAAK,IAAIgB,OAAO,GAAGA,OAAO1B,cAAc0B,QAAQ;EAC9C,IAAIX,aAAaK,IAAIE,IAAIJ,SAAS,IAAIR,OACpCc,MAAMN;OAENO,OAAOP;EAETA,aAAaM,MAAMC,QAAQ;CAC7B;CAEA,OAAOlB,SAASE,MAAMF,SAASQ,aAAaM,IAAIE,IAAIL,SAAS;AAC/D;;;;;;;;AASA,SAAAS,QAAAC,UAAAC,SAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CACE,MAAAC,QAAcxC,eAAe,CAAC;CAAC,IAAAyC;CAAA,IAAAC;CAAA,IAAAJ,EAAA,OAAAE,SAAAF,EAAA,OAAAF,YAAAE,EAAA,OAAAD,SAAA;EAErBI,WAAA;GACR,IAAI,CAACJ,SAAO;GAIZG,MAAKG,IAAK,CAAC;GACXH,MAAKG,IACH1C,WACEC,WAAW,GAAG;IAAAkC;IAAAQ,QAEJ9C,OAAM+C;IAAOC,cACP/C,aAAYgD;GAC5B,CAAC,GACD,IACA,OACAzB,KAAAA,GACAvB,aAAYgD,KACd,CACF;GAAC,aAEM;IACLlD,gBAAgB2C,KAAK;GAAC;EACvB;EACAE,KAAA;GAACF;GAAOJ;GAAUC;EAAO;EAACC,EAAA,KAAAE;EAAAF,EAAA,KAAAF;EAAAE,EAAA,KAAAD;EAAAC,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAA;EAAAD,KAAAH,EAAA;EAAAI,KAAAJ,EAAA;CAAA;CAvB7B1C,UAAU6C,IAuBPC,EAA0B;CAAC,OAEvBF;AAAK"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//#region src/indicator/paths.ts
|
|
2
|
+
const PLACES = 3;
|
|
3
|
+
/** A full turn, in radians. */
|
|
4
|
+
const TURN = 2 * Math.PI;
|
|
5
|
+
const DOT = .001;
|
|
6
|
+
const WAVE_AMPLITUDE = 3;
|
|
7
|
+
const WAVE_HEIGHT = 10;
|
|
8
|
+
const WAVE_ORIGIN = 2;
|
|
9
|
+
const POINTS_PER_WAVELENGTH = 16;
|
|
10
|
+
const POINTS_PER_RING_WAVE = 20;
|
|
11
|
+
/**
|
|
12
|
+
* An arc of the circle about (`centre`, `centre`), clockwise from `from` for
|
|
13
|
+
* `sweep` radians, both measured from the top. A sweep of nothing draws a dot,
|
|
14
|
+
* as web's zero-length dash does, and a full turn stops short of closing by
|
|
15
|
+
* a hair the round caps cover, since an arc that ends where it starts is no
|
|
16
|
+
* arc at all.
|
|
17
|
+
*/
|
|
18
|
+
function arcPath(centre, radius, from, sweep) {
|
|
19
|
+
"worklet";
|
|
20
|
+
const span = Math.min(Math.max(sweep, DOT), TURN - DOT);
|
|
21
|
+
const large = span > Math.PI ? 1 : 0;
|
|
22
|
+
function point(angle) {
|
|
23
|
+
const x = centre + radius * Math.sin(angle);
|
|
24
|
+
const y = centre - radius * Math.cos(angle);
|
|
25
|
+
return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`;
|
|
26
|
+
}
|
|
27
|
+
return `M${point(from)}A${radius} ${radius} 0 ${large} 1 ${point(from + span)}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A stretch of the wave along a line, from `from` to `to` across its row, on a
|
|
31
|
+
* wave `wavelength` long that rises first from `WAVE_ORIGIN`, as web's does.
|
|
32
|
+
* Nothing when the stretch is empty, which is how a bar that has not yet
|
|
33
|
+
* reached the row, or has left it, draws nothing.
|
|
34
|
+
*/
|
|
35
|
+
function wavePath(wavelength, from, to) {
|
|
36
|
+
"worklet";
|
|
37
|
+
if (to <= from) return "";
|
|
38
|
+
function point(x) {
|
|
39
|
+
const crest = Math.sin(TURN * (x - WAVE_ORIGIN) / wavelength);
|
|
40
|
+
const y = WAVE_HEIGHT / 2 - WAVE_AMPLITUDE * crest;
|
|
41
|
+
return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`;
|
|
42
|
+
}
|
|
43
|
+
const step = wavelength / POINTS_PER_WAVELENGTH;
|
|
44
|
+
let path = `M${point(from)}`;
|
|
45
|
+
for (let index = Math.floor((from - WAVE_ORIGIN) / step) + 1; WAVE_ORIGIN + index * step < to; index++) path += `L${point(WAVE_ORIGIN + index * step)}`;
|
|
46
|
+
return `${path}L${point(to)}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A stretch of a ring's wave, clockwise from `from` for `sweep` radians, both
|
|
50
|
+
* measured from the top, as `arcPath` takes them. `phase` is the angle the
|
|
51
|
+
* wave rises from, which turns with a spinning ring so its crests turn with
|
|
52
|
+
* it. A sweep of nothing draws a dot, as an arc's does.
|
|
53
|
+
*/
|
|
54
|
+
function wavyArcPath(wave, phase, from, sweep) {
|
|
55
|
+
"worklet";
|
|
56
|
+
const { amplitude, centre, radius, waves } = wave;
|
|
57
|
+
function point(angle) {
|
|
58
|
+
const reach = radius + amplitude * Math.sin(waves * (angle - phase));
|
|
59
|
+
const x = centre + reach * Math.sin(angle);
|
|
60
|
+
const y = centre - reach * Math.cos(angle);
|
|
61
|
+
return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`;
|
|
62
|
+
}
|
|
63
|
+
const step = TURN / (waves * POINTS_PER_RING_WAVE);
|
|
64
|
+
const to = from + Math.min(Math.max(sweep, DOT), TURN);
|
|
65
|
+
let path = `M${point(from)}`;
|
|
66
|
+
for (let index = Math.floor(from / step) + 1; index * step < to; index++) path += `L${point(index * step)}`;
|
|
67
|
+
return `${path}L${point(to)}`;
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { TURN, WAVE_HEIGHT, WAVE_ORIGIN, arcPath, wavePath, wavyArcPath };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","names":["THICKNESS","PLACES","TURN","Math","PI","DOT","WAVE_AMPLITUDE","WAVE_HEIGHT","WAVE_ORIGIN","POINTS_PER_WAVELENGTH","POINTS_PER_RING_WAVE","RingWave","amplitude","centre","radius","waves","arcPath","from","sweep","span","min","max","large","point","angle","x","sin","y","cos","toFixed","wavePath","wavelength","to","crest","step","path","index","floor","wavyArcPath","wave","phase","reach"],"sources":["../../src/indicator/paths.ts"],"sourcesContent":["import { THICKNESS } from './styles'\n\n// The paths a progress indicator draws its curves along: an arc of a circle,\n// an arc of a circle with a wave round it, and a wave along a line. Each is\n// built from where it starts and how far it runs, so a curve that moves is a\n// new path each frame rather than a dash pattern that moves.\n//\n// Web cuts its arcs and waves out of whole curves with `stroke-dasharray`,\n// normalised by `pathLength` so a dash reads as a share of the way round.\n// react-native-svg has no `pathLength`, and a dash pattern Reanimated moves\n// frame by frame is the least reliable of an svg's props across iOS, Android\n// and the web, so here the cut is made in the path itself, which every\n// platform redraws alike. The geometry is web's: the same circles, the same\n// waves, and the same lengths along them.\n//\n// Every builder is a worklet, so it runs on the UI thread with the animation\n// that moves it, and a test can call it as it stands. Each is whole on its\n// own, its helpers nested inside it: a worklet captures what it calls when the\n// module is evaluated, so one calling another declared below it would be\n// handed nothing, and the linter orders declarations by name, not by use.\n\n// Three decimal places, which keeps a path short without moving a point by\n// anything a screen could show, as web rounds its own.\nconst PLACES = 3\n\n/** A full turn, in radians. */\nconst TURN = 2 * Math.PI\n\n// The least an arc runs. Web's zero-length dash still draws, as a dot the\n// size of the stroke, since a round cap is drawn at either end of a dash of\n// any length; an svg path of no length draws nothing at all, so the arc is\n// given this much instead, which is the same dot.\nconst DOT = 0.001\n\n// The wave along the line: its amplitude either side of the middle, and the\n// row it is drawn in, which is the amplitude either side plus the stroke.\n// It starts half a stroke in, so its round cap starts at the row's edge.\nconst WAVE_AMPLITUDE = 3\nconst WAVE_HEIGHT = WAVE_AMPLITUDE * 2 + THICKNESS\nconst WAVE_ORIGIN = THICKNESS / 2\n\n// How many points a wavelength is drawn through: web draws a line's wave as\n// cubic half waves and a ring's as twenty points to a wave. A wave cut short\n// partway through a cubic would need it split, so both are drawn as points\n// here, finely enough that the joins are below what the 4dp stroke shows.\nconst POINTS_PER_WAVELENGTH = 16\nconst POINTS_PER_RING_WAVE = 20\n\n/** A circle a ring's wave runs round, and the wave. */\ntype RingWave = {\n /** How far the wave rises either side of the circle. */\n readonly amplitude: number\n /** The centre, on both axes. */\n readonly centre: number\n /** The circle's radius. */\n readonly radius: number\n /** How many crests the wave has round the whole circle. */\n readonly waves: number\n}\n\n/**\n * An arc of the circle about (`centre`, `centre`), clockwise from `from` for\n * `sweep` radians, both measured from the top. A sweep of nothing draws a dot,\n * as web's zero-length dash does, and a full turn stops short of closing by\n * a hair the round caps cover, since an arc that ends where it starts is no\n * arc at all.\n */\nfunction arcPath(centre: number, radius: number, from: number, sweep: number) {\n 'worklet'\n const span = Math.min(Math.max(sweep, DOT), TURN - DOT)\n const large = span > Math.PI ? 1 : 0\n\n // A point on the circle, `angle` radians clockwise from the top.\n function point(angle: number) {\n const x = centre + radius * Math.sin(angle)\n const y = centre - radius * Math.cos(angle)\n return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`\n }\n\n return `M${point(from)}A${radius} ${radius} 0 ${large} 1 ${point(from + span)}`\n}\n\n/**\n * A stretch of the wave along a line, from `from` to `to` across its row, on a\n * wave `wavelength` long that rises first from `WAVE_ORIGIN`, as web's does.\n * Nothing when the stretch is empty, which is how a bar that has not yet\n * reached the row, or has left it, draws nothing.\n */\nfunction wavePath(wavelength: number, from: number, to: number) {\n 'worklet'\n if (to <= from) {\n return ''\n }\n\n // The point on the wave above `x`.\n function point(x: number) {\n const crest = Math.sin((TURN * (x - WAVE_ORIGIN)) / wavelength)\n const y = WAVE_HEIGHT / 2 - WAVE_AMPLITUDE * crest\n return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`\n }\n\n const step = wavelength / POINTS_PER_WAVELENGTH\n let path = `M${point(from)}`\n for (\n let index = Math.floor((from - WAVE_ORIGIN) / step) + 1;\n WAVE_ORIGIN + index * step < to;\n index++\n ) {\n path += `L${point(WAVE_ORIGIN + index * step)}`\n }\n\n return `${path}L${point(to)}`\n}\n\n/**\n * A stretch of a ring's wave, clockwise from `from` for `sweep` radians, both\n * measured from the top, as `arcPath` takes them. `phase` is the angle the\n * wave rises from, which turns with a spinning ring so its crests turn with\n * it. A sweep of nothing draws a dot, as an arc's does.\n */\nfunction wavyArcPath(\n wave: RingWave,\n phase: number,\n from: number,\n sweep: number,\n) {\n 'worklet'\n const { amplitude, centre, radius, waves } = wave\n\n // The point on the wave at `angle`, clockwise from the top.\n function point(angle: number) {\n const reach = radius + amplitude * Math.sin(waves * (angle - phase))\n const x = centre + reach * Math.sin(angle)\n const y = centre - reach * Math.cos(angle)\n return `${x.toFixed(PLACES)} ${y.toFixed(PLACES)}`\n }\n\n const step = TURN / (waves * POINTS_PER_RING_WAVE)\n const to = from + Math.min(Math.max(sweep, DOT), TURN)\n let path = `M${point(from)}`\n for (let index = Math.floor(from / step) + 1; index * step < to; index++) {\n path += `L${point(index * step)}`\n }\n\n return `${path}L${point(to)}`\n}\n\nexport { arcPath, TURN, WAVE_HEIGHT, WAVE_ORIGIN, wavePath, wavyArcPath }\nexport type { RingWave }\n"],"mappings":";AAuBA,MAAMC,SAAS;;AAGf,MAAMC,OAAO,IAAIC,KAAKC;AAMtB,MAAMC,MAAM;AAKZ,MAAMC,iBAAiB;AACvB,MAAMC,cAAcD;AACpB,MAAME,cAAAA;AAMN,MAAMC,wBAAwB;AAC9B,MAAMC,uBAAuB;;;;;;;;AAqB7B,SAASM,QAAQH,QAAgBC,QAAgBG,MAAcC,OAAe;CAC5E;CACA,MAAMC,OAAOhB,KAAKiB,IAAIjB,KAAKkB,IAAIH,OAAOb,GAAG,GAAGH,OAAOG,GAAG;CACtD,MAAMiB,QAAQH,OAAOhB,KAAKC,KAAK,IAAI;CAGnC,SAASmB,MAAMC,OAAe;EAC5B,MAAMC,IAAIZ,SAASC,SAASX,KAAKuB,IAAIF,KAAK;EAC1C,MAAMG,IAAId,SAASC,SAASX,KAAKyB,IAAIJ,KAAK;EAC1C,OAAO,GAAGC,EAAEI,QAAQ5B,MAAM,EAAC,GAAI0B,EAAEE,QAAQ5B,MAAM;CACjD;CAEA,OAAO,IAAIsB,MAAMN,IAAI,EAAC,GAAIH,OAAM,GAAIA,OAAM,KAAMQ,MAAK,KAAMC,MAAMN,OAAOE,IAAI;AAC9E;;;;;;;AAQA,SAASW,SAASC,YAAoBd,MAAce,IAAY;CAC9D;CACA,IAAIA,MAAMf,MACR,OAAO;CAIT,SAASM,MAAME,GAAW;EACxB,MAAMQ,QAAQ9B,KAAKuB,IAAKxB,QAAQuB,IAAIjB,eAAgBuB,UAAU;EAC9D,MAAMJ,IAAIpB,cAAc,IAAID,iBAAiB2B;EAC7C,OAAO,GAAGR,EAAEI,QAAQ5B,MAAM,EAAC,GAAI0B,EAAEE,QAAQ5B,MAAM;CACjD;CAEA,MAAMiC,OAAOH,aAAatB;CAC1B,IAAI0B,OAAO,IAAIZ,MAAMN,IAAI;CACzB,KACE,IAAImB,QAAQjC,KAAKkC,OAAOpB,OAAOT,eAAe0B,IAAI,IAAI,GACtD1B,cAAc4B,QAAQF,OAAOF,IAC7BI,SAEAD,QAAQ,IAAIZ,MAAMf,cAAc4B,QAAQF,IAAI;CAG9C,OAAO,GAAGC,KAAI,GAAIZ,MAAMS,EAAE;AAC5B;;;;;;;AAQA,SAASM,YACPC,MACAC,OACAvB,MACAC,OACA;CACA;CACA,MAAM,EAAEN,WAAWC,QAAQC,QAAQC,UAAUwB;CAG7C,SAAShB,MAAMC,OAAe;EAC5B,MAAMiB,QAAQ3B,SAASF,YAAYT,KAAKuB,IAAIX,SAASS,QAAQgB,MAAM;EACnE,MAAMf,IAAIZ,SAAS4B,QAAQtC,KAAKuB,IAAIF,KAAK;EACzC,MAAMG,IAAId,SAAS4B,QAAQtC,KAAKyB,IAAIJ,KAAK;EACzC,OAAO,GAAGC,EAAEI,QAAQ5B,MAAM,EAAC,GAAI0B,EAAEE,QAAQ5B,MAAM;CACjD;CAEA,MAAMiC,OAAOhC,QAAQa,QAAQL;CAC7B,MAAMsB,KAAKf,OAAOd,KAAKiB,IAAIjB,KAAKkB,IAAIH,OAAOb,GAAG,GAAGH,IAAI;CACrD,IAAIiC,OAAO,IAAIZ,MAAMN,IAAI;CACzB,KAAK,IAAImB,QAAQjC,KAAKkC,MAAMpB,OAAOiB,IAAI,IAAI,GAAGE,QAAQF,OAAOF,IAAII,SAC/DD,QAAQ,IAAIZ,MAAMa,QAAQF,IAAI;CAGhC,OAAO,GAAGC,KAAI,GAAIZ,MAAMS,EAAE;AAC5B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { themedStyles } from "../theme/styles.js";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
/**
|
|
4
|
+
* The curve a determinate value moves along, Material's own, over the motion
|
|
5
|
+
* scale's `durationMedium1`.
|
|
6
|
+
*/
|
|
7
|
+
const DETERMINATE_EASING = [
|
|
8
|
+
.4,
|
|
9
|
+
0,
|
|
10
|
+
.6,
|
|
11
|
+
1
|
|
12
|
+
];
|
|
13
|
+
const styles = StyleSheet.create({
|
|
14
|
+
active: {
|
|
15
|
+
flexShrink: 0,
|
|
16
|
+
height: 4
|
|
17
|
+
},
|
|
18
|
+
label: {
|
|
19
|
+
flexShrink: 1,
|
|
20
|
+
minWidth: 0
|
|
21
|
+
},
|
|
22
|
+
line: {
|
|
23
|
+
alignItems: "center",
|
|
24
|
+
alignSelf: "stretch",
|
|
25
|
+
flexDirection: "row",
|
|
26
|
+
gap: 4,
|
|
27
|
+
height: 4
|
|
28
|
+
},
|
|
29
|
+
stop: {
|
|
30
|
+
flexShrink: 0,
|
|
31
|
+
height: 4,
|
|
32
|
+
width: 4
|
|
33
|
+
},
|
|
34
|
+
track: {
|
|
35
|
+
flexDirection: "row",
|
|
36
|
+
flexGrow: 1,
|
|
37
|
+
flexShrink: 1,
|
|
38
|
+
height: 4,
|
|
39
|
+
minWidth: 0,
|
|
40
|
+
overflow: "hidden"
|
|
41
|
+
},
|
|
42
|
+
value: {
|
|
43
|
+
flexShrink: 0,
|
|
44
|
+
marginStart: "auto"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const stylesOf = themedStyles(({ radii, spacing }) => StyleSheet.create({
|
|
48
|
+
active: { borderRadius: radii.pill },
|
|
49
|
+
labels: {
|
|
50
|
+
alignItems: "baseline",
|
|
51
|
+
flexDirection: "row",
|
|
52
|
+
gap: spacing.sm,
|
|
53
|
+
justifyContent: "space-between"
|
|
54
|
+
},
|
|
55
|
+
root: { gap: spacing.xs },
|
|
56
|
+
stop: { borderRadius: radii.circle },
|
|
57
|
+
track: { borderRadius: radii.pill }
|
|
58
|
+
}));
|
|
59
|
+
//#endregion
|
|
60
|
+
export { DETERMINATE_EASING, styles, stylesOf };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=styles.js.map
|