@kanso-labs/react-native-kanso-ui 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/badge/index.js +9 -68
- package/dist/badge/index.js.map +1 -1
- package/dist/button/index.d.ts +2 -1
- package/dist/button/index.js +1 -1
- package/dist/button/index.js.map +1 -1
- package/dist/button/palettes.d.ts +2 -0
- package/dist/button/pending.js +17 -21
- package/dist/button/pending.js.map +1 -1
- package/dist/calendar/index.js +1 -1
- package/dist/components/autocomplete/index.js +28 -26
- package/dist/components/autocomplete/index.js.map +1 -1
- package/dist/components/avatar/index.d.ts +2 -2
- package/dist/components/avatar/index.js +2 -2
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/badge/index.d.ts +48 -0
- package/dist/components/badge/index.js +122 -0
- package/dist/components/badge/index.js.map +1 -0
- package/dist/{badge → components/badge}/styles.js +10 -6
- package/dist/components/badge/styles.js.map +1 -0
- package/dist/components/breadcrumbs/index.d.ts +66 -0
- package/dist/components/breadcrumbs/index.js +285 -0
- package/dist/components/breadcrumbs/index.js.map +1 -0
- package/dist/components/calendar/index.d.ts +1 -1
- package/dist/components/calendar/index.js +77 -38
- package/dist/components/calendar/index.js.map +1 -1
- package/dist/components/checkbox/index.d.ts +7 -1
- package/dist/components/checkbox/index.js +183 -138
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/checkbox-group/index.d.ts +93 -0
- package/dist/components/checkbox-group/index.js +306 -0
- package/dist/components/checkbox-group/index.js.map +1 -0
- package/dist/components/chip-group/index.d.ts +4 -3
- package/dist/components/chip-group/index.js +47 -45
- package/dist/components/chip-group/index.js.map +1 -1
- package/dist/components/code/index.d.ts +25 -0
- package/dist/components/code/index.js +87 -0
- package/dist/components/code/index.js.map +1 -0
- package/dist/components/container/index.d.ts +47 -0
- package/dist/components/container/index.js +85 -0
- package/dist/components/container/index.js.map +1 -0
- package/dist/components/copy-field/index.d.ts +5 -5
- package/dist/components/copy-field/index.js +30 -28
- package/dist/components/copy-field/index.js.map +1 -1
- package/dist/components/currency/index.d.ts +2 -2
- package/dist/components/currency/index.js +3 -1
- package/dist/components/currency/index.js.map +1 -1
- package/dist/components/fab/index.d.ts +80 -0
- package/dist/components/fab/index.js +231 -0
- package/dist/components/fab/index.js.map +1 -0
- package/dist/components/fab-menu/index.d.ts +120 -0
- package/dist/components/fab-menu/index.js +640 -0
- package/dist/components/fab-menu/index.js.map +1 -0
- package/dist/components/feed/index.d.ts +57 -0
- package/dist/components/feed/index.js +167 -0
- package/dist/components/feed/index.js.map +1 -0
- package/dist/components/keycap/index.d.ts +27 -0
- package/dist/components/keycap/index.js +136 -0
- package/dist/components/keycap/index.js.map +1 -0
- package/dist/components/list/index.d.ts +6 -5
- package/dist/components/list/index.js +97 -106
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/menu/index.d.ts +4 -3
- package/dist/components/menu/index.js +33 -40
- package/dist/components/menu/index.js.map +1 -1
- package/dist/components/meter/index.d.ts +2 -1
- package/dist/components/meter/index.js +9 -7
- package/dist/components/meter/index.js.map +1 -1
- package/dist/components/number-field/index.d.ts +7 -3
- package/dist/components/number-field/index.js +214 -192
- 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 +2 -2
- package/dist/components/product-icon/index.js.map +1 -1
- package/dist/components/progress-indicator/index.d.ts +2 -1
- package/dist/components/progress-indicator/index.js +15 -13
- package/dist/components/progress-indicator/index.js.map +1 -1
- package/dist/components/search-field/index.d.ts +3 -3
- package/dist/components/search-field/index.js +105 -104
- package/dist/components/search-field/index.js.map +1 -1
- package/dist/components/separator/index.d.ts +3 -2
- package/dist/components/separator/index.js +29 -26
- package/dist/components/separator/index.js.map +1 -1
- package/dist/components/sheet/index.d.ts +2 -2
- package/dist/components/sheet/index.js +22 -26
- package/dist/components/sheet/index.js.map +1 -1
- package/dist/components/snackbar/index.d.ts +3 -3
- package/dist/components/snackbar/index.js +36 -34
- package/dist/components/snackbar/index.js.map +1 -1
- package/dist/components/supporting-pane/index.d.ts +44 -0
- package/dist/components/supporting-pane/index.js +109 -0
- package/dist/components/supporting-pane/index.js.map +1 -0
- package/dist/components/tag/index.d.ts +58 -0
- package/dist/components/tag/index.js +205 -0
- package/dist/components/tag/index.js.map +1 -0
- package/dist/components/text/index.d.ts +1 -1
- package/dist/components/text/index.js +49 -21
- package/dist/components/text/index.js.map +1 -1
- package/dist/components/text-area/index.d.ts +145 -0
- package/dist/components/text-area/index.js +231 -0
- package/dist/components/text-area/index.js.map +1 -0
- package/dist/components/text-field/index.d.ts +6 -3
- package/dist/components/text-field/index.js +3 -0
- package/dist/components/text-field/index.js.map +1 -1
- package/dist/components/toolbar/index.d.ts +53 -0
- package/dist/components/toolbar/index.js +185 -0
- package/dist/components/toolbar/index.js.map +1 -0
- package/dist/control/group.js +23 -0
- package/dist/control/group.js.map +1 -0
- package/dist/fab/index.d.ts +19 -0
- package/dist/fab/index.js +147 -0
- package/dist/fab/index.js.map +1 -0
- package/dist/field/index.js +123 -79
- package/dist/field/index.js.map +1 -1
- package/dist/field/styles.js +18 -0
- package/dist/field/styles.js.map +1 -1
- package/dist/form/index.d.ts +1 -2
- package/dist/form/index.js +1 -1
- package/dist/form/index.js.map +1 -1
- package/dist/i18n/index.js +12 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/messages.js +367 -0
- package/dist/i18n/messages.js.map +1 -1
- package/dist/i18n/provider.d.ts +53 -0
- package/dist/i18n/provider.js +96 -0
- package/dist/i18n/provider.js.map +1 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +17 -3
- package/dist/indicator/index.js +1 -2
- package/dist/indicator/index.js.map +1 -1
- package/dist/mark/index.js +77 -117
- package/dist/mark/index.js.map +1 -1
- package/dist/mark/initials.js.map +1 -1
- package/dist/modal/index.js +59 -46
- package/dist/modal/index.js.map +1 -1
- package/dist/navigation/index.js +8 -5
- package/dist/navigation/index.js.map +1 -1
- package/dist/number/count.js +15 -0
- package/dist/number/count.js.map +1 -0
- package/dist/number/locale.js.map +1 -1
- package/dist/overlay/host.js +30 -21
- package/dist/overlay/host.js.map +1 -1
- package/dist/overlay/placement.js +3 -1
- package/dist/overlay/placement.js.map +1 -1
- package/dist/separator/index.js +7 -0
- package/dist/separator/index.js.map +1 -0
- package/dist/typography/size.js +14 -0
- package/dist/typography/size.js.map +1 -0
- package/package.json +1 -1
- package/dist/badge/styles.js.map +0 -1
|
@@ -5,13 +5,13 @@ type CopyFieldProps = Omit<ViewProps, 'children'> & {
|
|
|
5
5
|
/**
|
|
6
6
|
* What the button says once the value has been copied, and what is
|
|
7
7
|
* announced. It reverts to `copyLabel` on its own after a couple of
|
|
8
|
-
* seconds.
|
|
9
|
-
*
|
|
8
|
+
* seconds. Left out, it is the word for it in the `I18nProvider`'s locale,
|
|
9
|
+
* or the device's outside one, "Copied" in English.
|
|
10
10
|
*/
|
|
11
11
|
readonly copiedLabel?: string;
|
|
12
12
|
/**
|
|
13
|
-
* What the button says at rest.
|
|
14
|
-
*
|
|
13
|
+
* What the button says at rest. Left out, it is the word for it in the
|
|
14
|
+
* `I18nProvider`'s locale, or the device's outside one, "Copy" in English.
|
|
15
15
|
*/
|
|
16
16
|
readonly copyLabel?: string;
|
|
17
17
|
/**
|
|
@@ -54,7 +54,7 @@ type CopyFieldProps = Omit<ViewProps, 'children'> & {
|
|
|
54
54
|
*
|
|
55
55
|
* `style`, `ref` and `testID` land on the box.
|
|
56
56
|
*/
|
|
57
|
-
declare function CopyField({ copiedLabel, copyLabel, label, onCopied, onCopyFailed, style, value, ...props }: CopyFieldProps): import("react").JSX.Element;
|
|
57
|
+
declare function CopyField({ copiedLabel: ownCopiedLabel, copyLabel: ownCopyLabel, label, onCopied, onCopyFailed, style, value, ...props }: CopyFieldProps): import("react").JSX.Element;
|
|
58
58
|
//#endregion
|
|
59
59
|
export { CopyField, type CopyFieldProps };
|
|
60
60
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { useTheme } from "../../theme/scope.js";
|
|
|
2
2
|
import { composeStyles, themedStyles } from "../../theme/styles.js";
|
|
3
3
|
import { useFontFamily } from "../../typography/fonts.js";
|
|
4
4
|
import { useTypeRole } from "../../typography/roles.js";
|
|
5
|
+
import { useMessages } from "../../i18n/provider.js";
|
|
5
6
|
import { Button } from "../button/index.js";
|
|
6
7
|
import { useClipboardAvailable, writeClipboard } from "../../clipboard/index.js";
|
|
7
8
|
import { c } from "react/compiler-runtime";
|
|
@@ -81,43 +82,44 @@ function CopyField(t0) {
|
|
|
81
82
|
let label;
|
|
82
83
|
let onCopied;
|
|
83
84
|
let onCopyFailed;
|
|
85
|
+
let ownCopiedLabel;
|
|
86
|
+
let ownCopyLabel;
|
|
84
87
|
let props;
|
|
85
88
|
let style;
|
|
86
|
-
let t1;
|
|
87
|
-
let t2;
|
|
88
89
|
let value;
|
|
89
90
|
if ($[0] !== t0) {
|
|
90
|
-
({copiedLabel:
|
|
91
|
+
({copiedLabel: ownCopiedLabel, copyLabel: ownCopyLabel, label, onCopied, onCopyFailed, style, value, ...props} = t0);
|
|
91
92
|
$[0] = t0;
|
|
92
93
|
$[1] = label;
|
|
93
94
|
$[2] = onCopied;
|
|
94
95
|
$[3] = onCopyFailed;
|
|
95
|
-
$[4] =
|
|
96
|
-
$[5] =
|
|
97
|
-
$[6] =
|
|
98
|
-
$[7] =
|
|
96
|
+
$[4] = ownCopiedLabel;
|
|
97
|
+
$[5] = ownCopyLabel;
|
|
98
|
+
$[6] = props;
|
|
99
|
+
$[7] = style;
|
|
99
100
|
$[8] = value;
|
|
100
101
|
} else {
|
|
101
102
|
label = $[1];
|
|
102
103
|
onCopied = $[2];
|
|
103
104
|
onCopyFailed = $[3];
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
ownCopiedLabel = $[4];
|
|
106
|
+
ownCopyLabel = $[5];
|
|
107
|
+
props = $[6];
|
|
108
|
+
style = $[7];
|
|
108
109
|
value = $[8];
|
|
109
110
|
}
|
|
110
|
-
const
|
|
111
|
-
const
|
|
111
|
+
const messages = useMessages();
|
|
112
|
+
const copiedLabel = ownCopiedLabel ?? messages.copied;
|
|
113
|
+
const copyLabel = ownCopyLabel ?? messages.copy;
|
|
112
114
|
const theme = useTheme();
|
|
113
115
|
const themed = stylesOf(theme);
|
|
114
116
|
const bodySmall = useTypeRole("bodySmall");
|
|
115
117
|
const labelLarge = useTypeRole("labelLarge");
|
|
116
118
|
const mono = useFontFamily(theme.typography.fontFamilyMono);
|
|
117
119
|
const available = useClipboardAvailable();
|
|
118
|
-
let
|
|
120
|
+
let t1;
|
|
119
121
|
if ($[9] !== copiedLabel || $[10] !== onCopied || $[11] !== onCopyFailed || $[12] !== value) {
|
|
120
|
-
|
|
122
|
+
t1 = {
|
|
121
123
|
copiedLabel,
|
|
122
124
|
onCopied,
|
|
123
125
|
onCopyFailed,
|
|
@@ -127,20 +129,20 @@ function CopyField(t0) {
|
|
|
127
129
|
$[10] = onCopied;
|
|
128
130
|
$[11] = onCopyFailed;
|
|
129
131
|
$[12] = value;
|
|
130
|
-
$[13] =
|
|
131
|
-
} else
|
|
132
|
-
const { confirmation, onPress } = useCopy(
|
|
132
|
+
$[13] = t1;
|
|
133
|
+
} else t1 = $[13];
|
|
134
|
+
const { confirmation, onPress } = useCopy(t1);
|
|
133
135
|
const copied = confirmation !== void 0;
|
|
134
136
|
const shown = copied ? copiedLabel : copyLabel;
|
|
135
|
-
let
|
|
137
|
+
let t2;
|
|
136
138
|
if ($[14] !== label) {
|
|
137
|
-
|
|
139
|
+
t2 = label === void 0 ? void 0 : { "aria-label": label };
|
|
138
140
|
$[14] = label;
|
|
139
|
-
$[15] =
|
|
140
|
-
} else
|
|
141
|
-
let
|
|
141
|
+
$[15] = t2;
|
|
142
|
+
} else t2 = $[15];
|
|
143
|
+
let t3;
|
|
142
144
|
if ($[16] !== confirmation || $[17] !== copied || $[18] !== copiedLabel) {
|
|
143
|
-
|
|
145
|
+
t3 = Platform.OS === "web" ? /* @__PURE__ */ jsx(View, {
|
|
144
146
|
"aria-live": "polite",
|
|
145
147
|
...ATOMIC,
|
|
146
148
|
style: styles.announcement,
|
|
@@ -149,10 +151,10 @@ function CopyField(t0) {
|
|
|
149
151
|
$[16] = confirmation;
|
|
150
152
|
$[17] = copied;
|
|
151
153
|
$[18] = copiedLabel;
|
|
152
|
-
$[19] =
|
|
153
|
-
} else
|
|
154
|
+
$[19] = t3;
|
|
155
|
+
} else t3 = $[19];
|
|
154
156
|
return /* @__PURE__ */ jsxs(View, {
|
|
155
|
-
...
|
|
157
|
+
...t2,
|
|
156
158
|
role: label === void 0 ? void 0 : "group",
|
|
157
159
|
...props,
|
|
158
160
|
style: composeStyles(themed.root, style),
|
|
@@ -182,7 +184,7 @@ function CopyField(t0) {
|
|
|
182
184
|
children: copiedLabel
|
|
183
185
|
})]
|
|
184
186
|
})] }) : null,
|
|
185
|
-
|
|
187
|
+
t3
|
|
186
188
|
]
|
|
187
189
|
});
|
|
188
190
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ComponentRef","Ref","TextStyle","ViewProps","ViewStyle","useEffect","useRef","useState","AccessibilityInfo","Text","NativeText","Platform","StyleSheet","View","useClipboardAvailable","writeClipboard","useTheme","composeStyles","themedStyles","useFontFamily","useTypeRole","Button","COPIED_RESET_MS","RESERVE","accessibilityElementsHidden","importantForAccessibility","ATOMIC","const","styles","create","announcement","height","opacity","overflow","pointerEvents","position","width","button","alignSelf","reserve","alignItems","stylesOf","colors","radii","spacing","paddingHorizontal","md","root","backgroundColor","surface","borderColor","outline","borderRadius","sm","borderWidth","flexDirection","gap","paddingVertical","xs","value","flex","minWidth","valueText","color","onSurface","fontVariant","userSelect","writingDirection","CopyFieldProps","Omit","copiedLabel","copyLabel","label","onCopied","onCopyFailed","error","ref","CopyField","t0","$","_c","props","style","t1","t2","undefined","theme","themed","bodySmall","labelLarge","mono","typography","fontFamilyMono","available","t3","confirmation","onPress","useCopy","copied","shown","t4","t5","OS","fontFamily","reportCopied","thrown","setTimeout","copy","setCopy","timer","Symbol","for","clearTimeout","current","count","text","announceForAccessibility","confirm","then","t6","_c","NativeText"],"sources":["../../../src/components/copy-field/index.tsx"],"sourcesContent":["import type { ComponentRef, Ref } from 'react'\nimport type { TextStyle, ViewProps, ViewStyle } from 'react-native'\n\nimport { useEffect, useRef, useState } from 'react'\nimport {\n AccessibilityInfo,\n Text as NativeText,\n Platform,\n StyleSheet,\n View,\n} from 'react-native'\n\nimport { useClipboardAvailable, writeClipboard } from '../../clipboard'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useFontFamily } from '../../typography/fonts'\nimport { useTypeRole } from '../../typography/roles'\nimport { Button } from '../button'\n\n// No Material Design page draws a read-only value with a copy control, so it\n// follows the outlined text field, as kanso-ui's CopyField does: the 1dp rule\n// in the outline role, the surface behind it, and the control at its end. The\n// value is body small beside a 32dp text button, so the box is shorter than\n// the field's 56, which suits a value that is read rather than edited, and\n// its corner is the shape scale's small step for the same reason. The box has\n// no height of its own: it is the button's, or the value's once that is\n// taller, so a larger OS text size grows it rather than running the value out\n// of it.\n//\n// The copy goes through expo-clipboard, an optional peer. In an app without\n// it the field shows the value and no button, since a button that could not\n// copy would be one that lies about what it does.\n\n// How long the confirmation stands before the button offers the copy again,\n// as on web: long enough to be read without leaving the button lying about\n// what it will do.\nconst COPIED_RESET_MS = 2000\n\n// The two labels the button is as wide as: laid out at no height, and kept\n// from a screen reader.\nconst RESERVE: ViewProps = {\n accessibilityElementsHidden: true,\n 'aria-hidden': true,\n importantForAccessibility: 'no-hide-descendants',\n}\n\n// What a web-only atomic live region adds, so a confirmation is read whole.\n// React Native's types carry no `aria-atomic`, which React Native Web takes.\nconst ATOMIC = { 'aria-atomic': true } as const\n\nconst styles = StyleSheet.create({\n // Under React Native Web the confirmation is said through a live region, as\n // web's `<output>` says it, which is drawn nowhere and still read.\n announcement: {\n height: 1,\n opacity: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n width: 1,\n },\n // The button stretches to the width the column takes, which is the wider of\n // the two labels below it, so a copy swaps the label rather than resizing\n // the button. The button sits at the end of the row, so a width that grew\n // on press would move it under the finger that had just pressed it.\n button: {\n alignSelf: 'stretch',\n },\n // Holds both labels for their width alone.\n reserve: {\n alignItems: 'center',\n height: 0,\n overflow: 'hidden',\n },\n})\n\nconst stylesOf = themedStyles(({ colors, radii, spacing }) =>\n StyleSheet.create({\n // The button's own label padding at `xs`, so the reserve is exactly as\n // wide as the button drawing the wider label.\n reserve: {\n paddingHorizontal: spacing.md,\n },\n root: {\n alignItems: 'center',\n backgroundColor: colors.surface,\n borderColor: colors.outline,\n borderRadius: radii.sm,\n borderWidth: 1,\n flexDirection: 'row',\n gap: spacing.sm,\n paddingHorizontal: spacing.md,\n paddingVertical: spacing.xs,\n },\n // The column the value sits in, beside the button. `minWidth: 0` lets a\n // long unbroken value wrap rather than push the button out of the box.\n // The value keeps to the column's start, which is its left, or its right\n // in a right-to-left layout.\n value: {\n alignItems: 'flex-start',\n flex: 1,\n minWidth: 0,\n },\n // The value in the mono face at the body small role, where a `0` and an\n // `O`, an `l`, a `1` and an `I` have to be told apart, with ligatures off\n // so what shows is each character that is copied. It reads left to right\n // whatever the layout's direction, since it is a literal: a path, a flag\n // or a command whose leading `/`, `--` or `$` would otherwise move to the\n // other end. iOS and React Native Web take `writingDirection`; Android\n // takes the direction of the value's first letter, which is the value's\n // own. Under React Native Web one click selects the whole value, which is\n // what someone copying it by hand wants, and on iOS and Android a long\n // press offers the platform's own copy.\n valueText: {\n color: colors.onSurface,\n fontVariant: [\n 'no-common-ligatures',\n 'no-contextual',\n 'no-discretionary-ligatures',\n 'no-historical-ligatures',\n ],\n userSelect: 'all',\n writingDirection: 'ltr',\n },\n }),\n)\n\ntype CopyFieldProps = Omit<ViewProps, 'children'> & {\n /**\n * What the button says once the value has been copied, and what is\n * announced. It reverts to `copyLabel` on its own after a couple of\n * seconds.\n * @default 'Copied'\n */\n readonly copiedLabel?: string\n /**\n * What the button says at rest.\n * @default 'Copy'\n */\n readonly copyLabel?: string\n /**\n * What the value is, such as \"Repository URL\" or \"Access token\", for a\n * screen with more than one field. It names the field as a group and joins\n * the button's name, so a screen reader hears \"Copy Repository URL\" rather\n * than one \"Copy\" after another; it is not shown. The value itself stays\n * out of the button's name, since a token would be read aloud. Left out,\n * the button is \"Copy\" alone.\n */\n readonly label?: string\n /**\n * Called with the value after it reaches the clipboard, never before.\n * Named as web's is, where the root's own `onCopy` is taken.\n */\n readonly onCopied?: (value: string) => void\n /**\n * Called with the error when the value did not reach the clipboard: under\n * React Native Web, where the browser refuses the write, and anywhere\n * expo-clipboard turns out not to be installed after a press. Nothing is\n * announced and the button keeps offering the copy, so this is for an app\n * that wants to say more, in a snackbar say.\n */\n readonly onCopyFailed?: (error: unknown) => void\n readonly ref?: Ref<ComponentRef<typeof View>>\n /** The text shown, and the text copied. */\n readonly value: string\n}\n\n/**\n * A value to be read and taken away, such as a repository URL, a token or a\n * command. It shows the value in the mono face and copies it on request,\n * confirming on the button itself and to a screen reader.\n *\n * ```tsx\n * <CopyField label=\"Access token\" value=\"first.second.third\" />\n * ```\n *\n * The copy goes through expo-clipboard, which an app installs alongside the\n * library. Without it the field shows the value and no button.\n *\n * `style`, `ref` and `testID` land on the box.\n */\nfunction CopyField({\n copiedLabel = 'Copied',\n copyLabel = 'Copy',\n label,\n onCopied,\n onCopyFailed,\n style,\n value,\n ...props\n}: CopyFieldProps) {\n const theme = useTheme()\n const themed = stylesOf(theme)\n const bodySmall = useTypeRole('bodySmall')\n const labelLarge = useTypeRole('labelLarge')\n const mono = useFontFamily(theme.typography.fontFamilyMono)\n const available = useClipboardAvailable()\n const { confirmation, onPress } = useCopy({\n copiedLabel,\n onCopied,\n onCopyFailed,\n value,\n })\n const copied = confirmation !== undefined\n const shown = copied ? copiedLabel : copyLabel\n\n return (\n <View\n {...(label === undefined ? undefined : { 'aria-label': label })}\n role={label === undefined ? undefined : 'group'}\n {...props}\n style={composeStyles<ViewStyle>(themed.root, style)}\n >\n <View style={themed.value}>\n <NativeText\n style={composeStyles<TextStyle>(bodySmall, themed.valueText, {\n fontFamily: mono,\n })}\n >\n {value}\n </NativeText>\n </View>\n {available ? (\n <View>\n <Button\n aria-label={label === undefined ? undefined : `${shown} ${label}`}\n onPress={onPress}\n size=\"xs\"\n style={styles.button}\n variant=\"text\"\n >\n {shown}\n </Button>\n <View\n {...RESERVE}\n style={composeStyles<ViewStyle>(styles.reserve, themed.reserve)}\n >\n <NativeText style={labelLarge}>{copyLabel}</NativeText>\n <NativeText style={labelLarge}>{copiedLabel}</NativeText>\n </View>\n </View>\n ) : null}\n {Platform.OS === 'web' ? (\n <View aria-live=\"polite\" {...ATOMIC} style={styles.announcement}>\n {copied ? (\n <NativeText key={confirmation}>{copiedLabel}</NativeText>\n ) : null}\n </View>\n ) : null}\n </View>\n )\n}\n\n/**\n * Hands the call site the value it copied. What its handler throws is raised\n * on its own, outside the copy, as a throw from a press handler is, so it\n * reaches the app's error reporting rather than rejecting a promise nothing\n * awaits, which React Native reports nowhere in a release build.\n */\nfunction reportCopied(\n onCopied: ((value: string) => void) | undefined,\n value: string,\n) {\n if (onCopied === undefined) {\n return\n }\n\n try {\n onCopied(value)\n } catch (thrown) {\n setTimeout(() => {\n throw thrown\n }, 0)\n }\n}\n\n/**\n * Copies the value on press and holds the confirmation for the dwell.\n * `confirmation` counts the copies while the button says Copied, and is\n * `undefined` otherwise.\n */\nfunction useCopy({\n copiedLabel,\n onCopied,\n onCopyFailed,\n value,\n}: {\n readonly copiedLabel: string\n readonly onCopied: ((value: string) => void) | undefined\n readonly onCopyFailed: ((error: unknown) => void) | undefined\n readonly value: string\n}) {\n // What the last copy put on the clipboard, and how many copies there have\n // been. The confirmation is that text still being the value shown, so a\n // value that changes inside the dwell, a token regenerated say, takes the\n // label back to the copy at once rather than confirming a copy of something\n // else. The count gives each confirmation its own node in the live region,\n // so a second copy inside the dwell is said again.\n const [copy, setCopy] = useState<null | {\n readonly count: number\n readonly text: string\n }>(null)\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n // Without this, a field unmounted inside the dwell leaves a timer holding a\n // state update for a tree that is gone.\n useEffect(\n () => () => {\n clearTimeout(timer.current)\n },\n [],\n )\n\n // The field's own state first, the dwell and the announcement included, and\n // the call site's handler after it, so a handler that throws cannot leave\n // the button on Copied for good.\n const confirm = () => {\n clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setCopy(null)\n }, COPIED_RESET_MS)\n setCopy((current) => ({\n count: (current?.count ?? 0) + 1,\n text: value,\n }))\n\n // VoiceOver and TalkBack stay on the button, and a label changing under\n // them is not reliably read, so the confirmation is said outright. Under\n // React Native Web the field's live region says it.\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n AccessibilityInfo.announceForAccessibility(copiedLabel)\n }\n\n reportCopied(onCopied, value)\n }\n\n return {\n confirmation: copy?.text === value ? copy.count : undefined,\n onPress: () => {\n void writeClipboard(value).then(confirm, (error: unknown) => {\n onCopyFailed?.(error)\n })\n },\n }\n}\n\nexport { CopyField }\nexport type { CopyFieldProps }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ComponentRef","Ref","TextStyle","ViewProps","ViewStyle","useEffect","useRef","useState","AccessibilityInfo","Text","NativeText","Platform","StyleSheet","View","useClipboardAvailable","writeClipboard","useMessages","useTheme","composeStyles","themedStyles","useFontFamily","useTypeRole","Button","COPIED_RESET_MS","RESERVE","accessibilityElementsHidden","importantForAccessibility","ATOMIC","const","styles","create","announcement","height","opacity","overflow","pointerEvents","position","width","button","alignSelf","reserve","alignItems","stylesOf","colors","radii","spacing","paddingHorizontal","md","root","backgroundColor","surface","borderColor","outline","borderRadius","sm","borderWidth","flexDirection","gap","paddingVertical","xs","value","flex","minWidth","valueText","color","onSurface","fontVariant","userSelect","writingDirection","CopyFieldProps","Omit","copiedLabel","copyLabel","label","onCopied","onCopyFailed","error","ref","CopyField","t0","$","_c","ownCopiedLabel","ownCopyLabel","props","style","messages","copied","copy","theme","themed","bodySmall","labelLarge","mono","typography","fontFamilyMono","available","t1","confirmation","onPress","useCopy","undefined","shown","t2","t3","OS","fontFamily","reportCopied","thrown","setTimeout","setCopy","timer","Symbol","for","clearTimeout","current","count","text","announceForAccessibility","confirm","t4","t5","then","t6","_c","NativeText"],"sources":["../../../src/components/copy-field/index.tsx"],"sourcesContent":["import type { ComponentRef, Ref } from 'react'\nimport type { TextStyle, ViewProps, ViewStyle } from 'react-native'\n\nimport { useEffect, useRef, useState } from 'react'\nimport {\n AccessibilityInfo,\n Text as NativeText,\n Platform,\n StyleSheet,\n View,\n} from 'react-native'\n\nimport { useClipboardAvailable, writeClipboard } from '../../clipboard'\nimport { useMessages } from '../../i18n/provider'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useFontFamily } from '../../typography/fonts'\nimport { useTypeRole } from '../../typography/roles'\nimport { Button } from '../button'\n\n// No Material Design page draws a read-only value with a copy control, so it\n// follows the outlined text field, as kanso-ui's CopyField does: the 1dp rule\n// in the outline role, the surface behind it, and the control at its end. The\n// value is body small beside a 32dp text button, so the box is shorter than\n// the field's 56, which suits a value that is read rather than edited, and\n// its corner is the shape scale's small step for the same reason. The box has\n// no height of its own: it is the button's, or the value's once that is\n// taller, so a larger OS text size grows it rather than running the value out\n// of it.\n//\n// The copy goes through expo-clipboard, an optional peer. In an app without\n// it the field shows the value and no button, since a button that could not\n// copy would be one that lies about what it does.\n\n// How long the confirmation stands before the button offers the copy again,\n// as on web: long enough to be read without leaving the button lying about\n// what it will do.\nconst COPIED_RESET_MS = 2000\n\n// The two labels the button is as wide as: laid out at no height, and kept\n// from a screen reader.\nconst RESERVE: ViewProps = {\n accessibilityElementsHidden: true,\n 'aria-hidden': true,\n importantForAccessibility: 'no-hide-descendants',\n}\n\n// What a web-only atomic live region adds, so a confirmation is read whole.\n// React Native's types carry no `aria-atomic`, which React Native Web takes.\nconst ATOMIC = { 'aria-atomic': true } as const\n\nconst styles = StyleSheet.create({\n // Under React Native Web the confirmation is said through a live region, as\n // web's `<output>` says it, which is drawn nowhere and still read.\n announcement: {\n height: 1,\n opacity: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n width: 1,\n },\n // The button stretches to the width the column takes, which is the wider of\n // the two labels below it, so a copy swaps the label rather than resizing\n // the button. The button sits at the end of the row, so a width that grew\n // on press would move it under the finger that had just pressed it.\n button: {\n alignSelf: 'stretch',\n },\n // Holds both labels for their width alone.\n reserve: {\n alignItems: 'center',\n height: 0,\n overflow: 'hidden',\n },\n})\n\nconst stylesOf = themedStyles(({ colors, radii, spacing }) =>\n StyleSheet.create({\n // The button's own label padding at `xs`, so the reserve is exactly as\n // wide as the button drawing the wider label.\n reserve: {\n paddingHorizontal: spacing.md,\n },\n root: {\n alignItems: 'center',\n backgroundColor: colors.surface,\n borderColor: colors.outline,\n borderRadius: radii.sm,\n borderWidth: 1,\n flexDirection: 'row',\n gap: spacing.sm,\n paddingHorizontal: spacing.md,\n paddingVertical: spacing.xs,\n },\n // The column the value sits in, beside the button. `minWidth: 0` lets a\n // long unbroken value wrap rather than push the button out of the box.\n // The value keeps to the column's start, which is its left, or its right\n // in a right-to-left layout.\n value: {\n alignItems: 'flex-start',\n flex: 1,\n minWidth: 0,\n },\n // The value in the mono face at the body small role, where a `0` and an\n // `O`, an `l`, a `1` and an `I` have to be told apart, with ligatures off\n // so what shows is each character that is copied. It reads left to right\n // whatever the layout's direction, since it is a literal: a path, a flag\n // or a command whose leading `/`, `--` or `$` would otherwise move to the\n // other end. iOS and React Native Web take `writingDirection`; Android\n // takes the direction of the value's first letter, which is the value's\n // own. Under React Native Web one click selects the whole value, which is\n // what someone copying it by hand wants, and on iOS and Android a long\n // press offers the platform's own copy.\n valueText: {\n color: colors.onSurface,\n fontVariant: [\n 'no-common-ligatures',\n 'no-contextual',\n 'no-discretionary-ligatures',\n 'no-historical-ligatures',\n ],\n userSelect: 'all',\n writingDirection: 'ltr',\n },\n }),\n)\n\ntype CopyFieldProps = Omit<ViewProps, 'children'> & {\n /**\n * What the button says once the value has been copied, and what is\n * announced. It reverts to `copyLabel` on its own after a couple of\n * seconds. Left out, it is the word for it in the `I18nProvider`'s locale,\n * or the device's outside one, \"Copied\" in English.\n */\n readonly copiedLabel?: string\n /**\n * What the button says at rest. Left out, it is the word for it in the\n * `I18nProvider`'s locale, or the device's outside one, \"Copy\" in English.\n */\n readonly copyLabel?: string\n /**\n * What the value is, such as \"Repository URL\" or \"Access token\", for a\n * screen with more than one field. It names the field as a group and joins\n * the button's name, so a screen reader hears \"Copy Repository URL\" rather\n * than one \"Copy\" after another; it is not shown. The value itself stays\n * out of the button's name, since a token would be read aloud. Left out,\n * the button is \"Copy\" alone.\n */\n readonly label?: string\n /**\n * Called with the value after it reaches the clipboard, never before.\n * Named as web's is, where the root's own `onCopy` is taken.\n */\n readonly onCopied?: (value: string) => void\n /**\n * Called with the error when the value did not reach the clipboard: under\n * React Native Web, where the browser refuses the write, and anywhere\n * expo-clipboard turns out not to be installed after a press. Nothing is\n * announced and the button keeps offering the copy, so this is for an app\n * that wants to say more, in a snackbar say.\n */\n readonly onCopyFailed?: (error: unknown) => void\n readonly ref?: Ref<ComponentRef<typeof View>>\n /** The text shown, and the text copied. */\n readonly value: string\n}\n\n/**\n * A value to be read and taken away, such as a repository URL, a token or a\n * command. It shows the value in the mono face and copies it on request,\n * confirming on the button itself and to a screen reader.\n *\n * ```tsx\n * <CopyField label=\"Access token\" value=\"first.second.third\" />\n * ```\n *\n * The copy goes through expo-clipboard, which an app installs alongside the\n * library. Without it the field shows the value and no button.\n *\n * `style`, `ref` and `testID` land on the box.\n */\nfunction CopyField({\n copiedLabel: ownCopiedLabel,\n copyLabel: ownCopyLabel,\n label,\n onCopied,\n onCopyFailed,\n style,\n value,\n ...props\n}: CopyFieldProps) {\n const messages = useMessages()\n const copiedLabel = ownCopiedLabel ?? messages.copied\n const copyLabel = ownCopyLabel ?? messages.copy\n const theme = useTheme()\n const themed = stylesOf(theme)\n const bodySmall = useTypeRole('bodySmall')\n const labelLarge = useTypeRole('labelLarge')\n const mono = useFontFamily(theme.typography.fontFamilyMono)\n const available = useClipboardAvailable()\n const { confirmation, onPress } = useCopy({\n copiedLabel,\n onCopied,\n onCopyFailed,\n value,\n })\n const copied = confirmation !== undefined\n const shown = copied ? copiedLabel : copyLabel\n\n return (\n <View\n {...(label === undefined ? undefined : { 'aria-label': label })}\n role={label === undefined ? undefined : 'group'}\n {...props}\n style={composeStyles<ViewStyle>(themed.root, style)}\n >\n <View style={themed.value}>\n <NativeText\n style={composeStyles<TextStyle>(bodySmall, themed.valueText, {\n fontFamily: mono,\n })}\n >\n {value}\n </NativeText>\n </View>\n {available ? (\n <View>\n <Button\n aria-label={label === undefined ? undefined : `${shown} ${label}`}\n onPress={onPress}\n size=\"xs\"\n style={styles.button}\n variant=\"text\"\n >\n {shown}\n </Button>\n <View\n {...RESERVE}\n style={composeStyles<ViewStyle>(styles.reserve, themed.reserve)}\n >\n <NativeText style={labelLarge}>{copyLabel}</NativeText>\n <NativeText style={labelLarge}>{copiedLabel}</NativeText>\n </View>\n </View>\n ) : null}\n {Platform.OS === 'web' ? (\n <View aria-live=\"polite\" {...ATOMIC} style={styles.announcement}>\n {copied ? (\n <NativeText key={confirmation}>{copiedLabel}</NativeText>\n ) : null}\n </View>\n ) : null}\n </View>\n )\n}\n\n/**\n * Hands the call site the value it copied. What its handler throws is raised\n * on its own, outside the copy, as a throw from a press handler is, so it\n * reaches the app's error reporting rather than rejecting a promise nothing\n * awaits, which React Native reports nowhere in a release build.\n */\nfunction reportCopied(\n onCopied: ((value: string) => void) | undefined,\n value: string,\n) {\n if (onCopied === undefined) {\n return\n }\n\n try {\n onCopied(value)\n } catch (thrown) {\n setTimeout(() => {\n throw thrown\n }, 0)\n }\n}\n\n/**\n * Copies the value on press and holds the confirmation for the dwell.\n * `confirmation` counts the copies while the button says Copied, and is\n * `undefined` otherwise.\n */\nfunction useCopy({\n copiedLabel,\n onCopied,\n onCopyFailed,\n value,\n}: {\n readonly copiedLabel: string\n readonly onCopied: ((value: string) => void) | undefined\n readonly onCopyFailed: ((error: unknown) => void) | undefined\n readonly value: string\n}) {\n // What the last copy put on the clipboard, and how many copies there have\n // been. The confirmation is that text still being the value shown, so a\n // value that changes inside the dwell, a token regenerated say, takes the\n // label back to the copy at once rather than confirming a copy of something\n // else. The count gives each confirmation its own node in the live region,\n // so a second copy inside the dwell is said again.\n const [copy, setCopy] = useState<null | {\n readonly count: number\n readonly text: string\n }>(null)\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n // Without this, a field unmounted inside the dwell leaves a timer holding a\n // state update for a tree that is gone.\n useEffect(\n () => () => {\n clearTimeout(timer.current)\n },\n [],\n )\n\n // The field's own state first, the dwell and the announcement included, and\n // the call site's handler after it, so a handler that throws cannot leave\n // the button on Copied for good.\n const confirm = () => {\n clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setCopy(null)\n }, COPIED_RESET_MS)\n setCopy((current) => ({\n count: (current?.count ?? 0) + 1,\n text: value,\n }))\n\n // VoiceOver and TalkBack stay on the button, and a label changing under\n // them is not reliably read, so the confirmation is said outright. Under\n // React Native Web the field's live region says it.\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n AccessibilityInfo.announceForAccessibility(copiedLabel)\n }\n\n reportCopied(onCopied, value)\n }\n\n return {\n confirmation: copy?.text === value ? copy.count : undefined,\n onPress: () => {\n void writeClipboard(value).then(confirm, (error: unknown) => {\n onCopyFailed?.(error)\n })\n },\n }\n}\n\nexport { CopyField }\nexport type { CopyFieldProps }\n"],"mappings":"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"}
|
|
@@ -8,8 +8,8 @@ type CurrencyProps = Omit<TextProps, 'children'> & {
|
|
|
8
8
|
*/
|
|
9
9
|
readonly currency?: string;
|
|
10
10
|
/**
|
|
11
|
-
* BCP 47 locale to format for. Defaults to the
|
|
12
|
-
*
|
|
11
|
+
* BCP 47 locale to format for. Defaults to the `I18nProvider`'s, or the
|
|
12
|
+
* device's outside one.
|
|
13
13
|
*/
|
|
14
14
|
readonly locale?: string;
|
|
15
15
|
readonly ref?: Ref<ComponentRef<typeof Text>>;
|
|
@@ -2,6 +2,7 @@ import { useTheme } from "../../theme/scope.js";
|
|
|
2
2
|
import { composeStyles, themedStyles } from "../../theme/styles.js";
|
|
3
3
|
import { useFontFamily } from "../../typography/fonts.js";
|
|
4
4
|
import { familyStyle, nativeWeight } from "../../typography/roles.js";
|
|
5
|
+
import { useLocaleOf } from "../../i18n/provider.js";
|
|
5
6
|
import { c } from "react/compiler-runtime";
|
|
6
7
|
import { StyleSheet, Text } from "react-native";
|
|
7
8
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -73,9 +74,10 @@ function Currency(t0) {
|
|
|
73
74
|
const theme = useTheme();
|
|
74
75
|
const own = stylesOf(theme);
|
|
75
76
|
const family = familyStyle(useFontFamily(theme.typography.fontFamilyMono));
|
|
77
|
+
const writtenIn = useLocaleOf(locale);
|
|
76
78
|
const amount = formatCurrency(value, {
|
|
77
79
|
currency,
|
|
78
|
-
locale,
|
|
80
|
+
locale: writtenIn,
|
|
79
81
|
sign
|
|
80
82
|
});
|
|
81
83
|
const T0 = Text;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ComponentRef","Ref","TextProps","NativeTextProps","TextStyle","Text","NativeText","StyleSheet","useTheme","composeStyles","themedStyles","useFontFamily","familyStyle","nativeWeight","stylesOf","colors","typography","create","base","fontVariant","fontWeight","weightMedium","negative","color","neutral","onSurface","positive","MINUS_SIGN","PLUS_SIGN","LOCALE_MINUS","CurrencyProps","Omit","currency","locale","ref","sign","CurrencySignDisplay","tone","CurrencyTone","value","ResolvedFormat","code","formatter","Intl","NumberFormat","zero","Currency","t0","$","_c","props","style","t1","t2","t3","undefined","theme","own","family","fontFamilyMono","amount","formatCurrency","T0","t4","resolveTone","shownAsZero","t5","text","formatters","Map","options","formatterFor","magnitude","format","Math","abs","mark","signFor","signed","key","cached","get","usable","isLocale","maximumFractionDigits","minimumFractionDigits","resolved","set","tag","supportedLocalesOf","length","replace","_c","NativeText"],"sources":["../../../src/components/currency/index.tsx"],"sourcesContent":["import type { ComponentRef, Ref } from 'react'\nimport type { TextProps as NativeTextProps, TextStyle } from 'react-native'\n\nimport { Text as NativeText, StyleSheet } from 'react-native'\n\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useFontFamily } from '../../typography/fonts'\nimport { familyStyle, nativeWeight } from '../../typography/roles'\n\n// Deliberately sets no font size or line height, as web's sets none. The same\n// amount turns up at several sizes, inside a list row, as a heading-scale\n// figure, as a total, differing in nothing else, so the size is the decision\n// of the Text around it. What must never vary is here: the mono face, tabular\n// figures so a column of them lines up on the decimal point, and the medium\n// weight that separates a figure from the prose around it.\n//\n// No Material Design page draws an amount. It is type alone: the mono face\n// and medium weight from the type tokens, tabular figures, and the on surface\n// role for the neutral tone. Positive and negative are the library's own\n// roles.\nconst stylesOf = themedStyles(({ colors, typography }) =>\n StyleSheet.create({\n base: {\n fontVariant: ['tabular-nums'],\n fontWeight: nativeWeight(typography.weightMedium),\n },\n negative: {\n color: colors.negative,\n },\n neutral: {\n color: colors.onSurface,\n },\n positive: {\n color: colors.positive,\n },\n }),\n)\n\n// U+2212 MINUS SIGN, not the U+002D hyphen Intl actually emits. The hyphen is\n// a typographic stand-in: it is narrower than the plus it pairs with and does\n// not sit on the same optical centre as the digits, which shows up as a\n// wobbling column the moment amounts are stacked.\nconst MINUS_SIGN = '\\u2212'\n\nconst PLUS_SIGN = '+'\n\n// The minus a locale writes, which is the hyphen in most and U+2212 already in\n// a few, such as Swedish and Persian.\nconst LOCALE_MINUS = /[-\\u2212]/u\n\ntype CurrencyProps = Omit<NativeTextProps, 'children'> & {\n /**\n * ISO 4217 code for the currency to format in.\n * @default 'USD'\n */\n readonly currency?: string\n /**\n * BCP 47 locale to format for. Defaults to the device's, which is the one\n * the runtime formats in when it is handed none.\n */\n readonly locale?: string\n readonly ref?: Ref<ComponentRef<typeof NativeText>>\n /**\n * When to show the sign. `auto` shows one only on negatives, `always` also\n * prefixes positives with a plus, and `never` shows neither, which leaves\n * colour as the only thing separating a credit from a debt, so reach for it\n * only where the direction is already stated some other way.\n *\n * An amount that shows as zero carries no sign under any of them, since\n * there is nothing owed or owing: `-0.004` reads `$0.00`, not `−$0.00`.\n * @default 'auto'\n */\n readonly sign?: CurrencySignDisplay\n /**\n * Which colour role to render in. `auto` follows the sign of the amount as\n * it is shown, so one that rounds to zero, float residue or a fraction of a\n * cent, reads as neutral rather than as a gain or a debt.\n *\n * The positive and negative roles are guaranteed legible on the surface\n * family, which is what the library's own tokens and every demo scheme are\n * held to. Over a container of its own, such as a filled card, the pair is\n * one colour family on top of another and is not guaranteed, so a custom\n * theme placing one there has to check that pair itself.\n * @default 'auto'\n */\n readonly tone?: CurrencyTone\n /** The amount, in the currency's major units. */\n readonly value: number\n}\n\ntype CurrencySignDisplay = 'always' | 'auto' | 'never'\n\ntype CurrencyTone = 'auto' | 'negative' | 'neutral' | 'positive'\n\n/**\n * A formatter, how it writes zero, and the currency code written beside its\n * number when it is the decimal fallback for a code the runtime rejected.\n */\ntype ResolvedFormat = {\n readonly code: null | string\n readonly formatter: Intl.NumberFormat\n readonly zero: string\n}\n\n/**\n * An amount of money, formatted for the reader's locale in the mono face with\n * tabular figures, so a column of amounts lines up on the decimal point.\n * Negatives carry a true minus sign, and the tone follows the sign unless\n * `tone` names one.\n *\n * ```tsx\n * <Text variant=\"bodyLarge\">\n * <Currency currency=\"EUR\" value={-42.5} />\n * </Text>\n * ```\n *\n * It sets no size of its own, so it takes the type of the Text around it:\n * put it in one, a heading-scale one for a larger figure. On its own it draws\n * at the platform's default size. The face is the mono family's first, once\n * the app has registered it with expo-font, and the system font until then,\n * with tabular figures either way.\n *\n * Web's `render` has no counterpart. `style`, `ref` and `testID` land on React\n * Native's own Text.\n */\nfunction Currency({\n currency = 'USD',\n locale,\n sign = 'auto',\n style,\n tone = 'auto',\n value,\n ...props\n}: CurrencyProps) {\n const theme = useTheme()\n const own = stylesOf(theme)\n const family = familyStyle(useFontFamily(theme.typography.fontFamilyMono))\n\n const amount = formatCurrency(value, { currency, locale, sign })\n\n return (\n <NativeText\n {...props}\n style={composeStyles<TextStyle>(\n own.base,\n family,\n own[resolveTone(value, tone, amount.shownAsZero)],\n style,\n )}\n >\n {amount.text}\n </NativeText>\n )\n}\n\n// Constructing an Intl.NumberFormat resolves the locale, loads the currency's\n// data and builds the pattern; formatting with one already built skips all\n// three. An instance is stateless once constructed, so one can be shared\n// across every component asking for the same locale and currency. The sign\n// is no part of the key, since every setting is drawn from the one formatter.\n//\n// What this saves is a mount and a changing amount, not an idle re-render:\n// the React Compiler already memoises the call below on the things it reads,\n// so a component re-rendered with the same props never reaches here. That\n// cache belongs to one component instance and counts `value` among its keys,\n// which leaves two cases it cannot cover: a column of rows, where every\n// instance has a cache of its own and each builds a formatter, and a single\n// amount that ticks, where each new value misses.\n//\n// Unbounded on purpose. The keys are the pairs an app actually uses, its\n// locales times its currencies, so the map settles at that size rather than\n// growing with renders or with values.\n//\n// A request the runtime rejects is cached too, under its own key, as the\n// fallback it resolved to, so a bad pair costs one failed construction rather\n// than one a render.\nconst formatters = new Map<string, ResolvedFormat>()\n\n// The amount is read off `format` alone, where web reads it off\n// `formatToParts` with the `signDisplay` settings that leave a zero bare.\n// Hermes has neither: it defines no `formatToParts` on iOS, ignores\n// `signDisplay` there, and takes only `auto`, `never`, `always` and\n// `exceptZero` on Android, so the `negative` web asks for throws. `format` is\n// what every runtime React Native runs on has.\n//\n// So the magnitude is formatted on its own, and it shows as zero exactly when\n// it reads as zero formatted by the same formatter, whatever digits the\n// locale writes. A sign, where one is shown, goes where the locale puts its\n// own minus. On a runtime with `formatToParts` the text this writes is the\n// text web writes, which the tests compare across locales.\nfunction formatCurrency(\n value: number,\n options: {\n currency: string\n locale: string | undefined\n sign: CurrencySignDisplay\n },\n) {\n const { code, formatter, zero } = formatterFor(\n options.locale,\n options.currency,\n )\n const magnitude = formatter.format(Math.abs(value))\n // Whether every digit shown is a zero, which is what the tone reads rather\n // than the raw value: the two never saw the same number before.\n const shownAsZero = magnitude === zero\n const mark = signFor(value, options.sign, shownAsZero)\n const amount = mark === undefined ? magnitude : signed(formatter, value, mark)\n\n return {\n shownAsZero,\n text: code === null || code === '' ? amount : `${amount} ${code}`,\n }\n}\n\n// A component that only shows an amount must not take the screen down with\n// it, and `new Intl.NumberFormat` throws for a malformed locale (`xx_YY`) and\n// for a currency code that is not one (`xx`, `US`). Valid input takes exactly\n// the path it always did; the rest falls back. A locale the runtime rejects,\n// an empty one among them, which is what clearing a locale control leaves,\n// gives way to the device's, and a currency code it rejects to the plain\n// number in that locale with the code written after it, so the amount still\n// reads and what was wrong with it stays visible.\n//\n// Nothing is logged: the library writes to the console nowhere, and a\n// warning an app cannot turn off is not its to emit.\nfunction formatterFor(locale: string | undefined, currency: string) {\n // Neither a BCP 47 locale nor an ISO 4217 code can contain a vertical bar,\n // so joining on one cannot make two different pairs share a key.\n const key = `${locale ?? ''}|${currency}`\n const cached = formatters.get(key)\n if (cached !== undefined) {\n return cached\n }\n\n const usable = locale !== undefined && isLocale(locale) ? locale : undefined\n let formatter: Intl.NumberFormat\n let code: null | string = null\n try {\n formatter = new Intl.NumberFormat(usable, { currency, style: 'currency' })\n } catch {\n formatter = new Intl.NumberFormat(usable, {\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n })\n code = currency\n }\n\n const resolved = { code, formatter, zero: formatter.format(0) }\n formatters.set(key, resolved)\n return resolved\n}\n\n// Whether the runtime takes `tag` as a locale at all — one it parses, whether\n// or not it has data for it, which it then fills in from the nearest it has.\nfunction isLocale(tag: string) {\n try {\n return Intl.NumberFormat.supportedLocalesOf(tag).length >= 0\n } catch {\n return false\n }\n}\n\n// An amount shown as zero is neither owed nor owing, so it takes the neutral\n// role whatever the raw value's sign: negative zero, a rounded-away debt and\n// float residue all read as settled. Otherwise the sign decides.\nfunction resolveTone(value: number, tone: CurrencyTone, shownAsZero: boolean) {\n if (tone !== 'auto') {\n return tone\n }\n if (shownAsZero) {\n return 'neutral'\n }\n if (value > 0) {\n return 'positive'\n }\n if (value < 0) {\n return 'negative'\n }\n return 'neutral'\n}\n\n// The amount with `mark` in place of the minus the locale writes on a\n// negative, so the mark sits where the locale puts its own sign: before the\n// symbol or after it, beside a direction mark in a right-to-left locale, or in\n// place of a space, as Swiss German's `CHF-12.50` does beside `CHF 12.50`. A\n// plus takes the same place, which is where Intl's own `exceptZero` puts it.\n// Only the sign is replaced: nothing a locale writes ahead of it, a symbol, a\n// code, a space or a direction mark, is a minus, so the first is the sign.\nfunction signed(formatter: Intl.NumberFormat, value: number, mark: string) {\n return formatter.format(-Math.abs(value)).replace(LOCALE_MINUS, mark)\n}\n\n// The sign an amount carries, if any. One shown as zero carries none under\n// any setting, since there is nothing owed or owing; otherwise a negative\n// takes a minus unless told to show none, and a positive a plus only when\n// told to always show one.\nfunction signFor(\n value: number,\n sign: CurrencySignDisplay,\n shownAsZero: boolean,\n) {\n if (shownAsZero || sign === 'never') {\n return undefined\n }\n if (value < 0) {\n return MINUS_SIGN\n }\n if (value > 0 && sign === 'always') {\n return PLUS_SIGN\n }\n return undefined\n}\n\nexport type { CurrencyProps, CurrencySignDisplay, CurrencyTone }\n\nexport { Currency }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ComponentRef","Ref","TextProps","NativeTextProps","TextStyle","Text","NativeText","StyleSheet","useLocaleOf","useTheme","composeStyles","themedStyles","useFontFamily","familyStyle","nativeWeight","stylesOf","colors","typography","create","base","fontVariant","fontWeight","weightMedium","negative","color","neutral","onSurface","positive","MINUS_SIGN","PLUS_SIGN","LOCALE_MINUS","CurrencyProps","Omit","currency","locale","ref","sign","CurrencySignDisplay","tone","CurrencyTone","value","ResolvedFormat","code","formatter","Intl","NumberFormat","zero","Currency","t0","$","_c","props","style","t1","t2","t3","undefined","theme","own","family","fontFamilyMono","writtenIn","amount","formatCurrency","T0","t4","resolveTone","shownAsZero","t5","text","formatters","Map","options","formatterFor","magnitude","format","Math","abs","mark","signFor","signed","key","cached","get","usable","isLocale","maximumFractionDigits","minimumFractionDigits","resolved","set","tag","supportedLocalesOf","length","replace","_c","NativeText"],"sources":["../../../src/components/currency/index.tsx"],"sourcesContent":["import type { ComponentRef, Ref } from 'react'\nimport type { TextProps as NativeTextProps, TextStyle } from 'react-native'\n\nimport { Text as NativeText, StyleSheet } from 'react-native'\n\nimport { useLocaleOf } from '../../i18n/provider'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useFontFamily } from '../../typography/fonts'\nimport { familyStyle, nativeWeight } from '../../typography/roles'\n\n// Deliberately sets no font size or line height, as web's sets none. The same\n// amount turns up at several sizes, inside a list row, as a heading-scale\n// figure, as a total, differing in nothing else, so the size is the decision\n// of the Text around it. What must never vary is here: the mono face, tabular\n// figures so a column of them lines up on the decimal point, and the medium\n// weight that separates a figure from the prose around it.\n//\n// No Material Design page draws an amount. It is type alone: the mono face\n// and medium weight from the type tokens, tabular figures, and the on surface\n// role for the neutral tone. Positive and negative are the library's own\n// roles.\nconst stylesOf = themedStyles(({ colors, typography }) =>\n StyleSheet.create({\n base: {\n fontVariant: ['tabular-nums'],\n fontWeight: nativeWeight(typography.weightMedium),\n },\n negative: {\n color: colors.negative,\n },\n neutral: {\n color: colors.onSurface,\n },\n positive: {\n color: colors.positive,\n },\n }),\n)\n\n// U+2212 MINUS SIGN, not the U+002D hyphen Intl actually emits. The hyphen is\n// a typographic stand-in: it is narrower than the plus it pairs with and does\n// not sit on the same optical centre as the digits, which shows up as a\n// wobbling column the moment amounts are stacked.\nconst MINUS_SIGN = '\\u2212'\n\nconst PLUS_SIGN = '+'\n\n// The minus a locale writes, which is the hyphen in most and U+2212 already in\n// a few, such as Swedish and Persian.\nconst LOCALE_MINUS = /[-\\u2212]/u\n\ntype CurrencyProps = Omit<NativeTextProps, 'children'> & {\n /**\n * ISO 4217 code for the currency to format in.\n * @default 'USD'\n */\n readonly currency?: string\n /**\n * BCP 47 locale to format for. Defaults to the `I18nProvider`'s, or the\n * device's outside one.\n */\n readonly locale?: string\n readonly ref?: Ref<ComponentRef<typeof NativeText>>\n /**\n * When to show the sign. `auto` shows one only on negatives, `always` also\n * prefixes positives with a plus, and `never` shows neither, which leaves\n * colour as the only thing separating a credit from a debt, so reach for it\n * only where the direction is already stated some other way.\n *\n * An amount that shows as zero carries no sign under any of them, since\n * there is nothing owed or owing: `-0.004` reads `$0.00`, not `−$0.00`.\n * @default 'auto'\n */\n readonly sign?: CurrencySignDisplay\n /**\n * Which colour role to render in. `auto` follows the sign of the amount as\n * it is shown, so one that rounds to zero, float residue or a fraction of a\n * cent, reads as neutral rather than as a gain or a debt.\n *\n * The positive and negative roles are guaranteed legible on the surface\n * family, which is what the library's own tokens and every demo scheme are\n * held to. Over a container of its own, such as a filled card, the pair is\n * one colour family on top of another and is not guaranteed, so a custom\n * theme placing one there has to check that pair itself.\n * @default 'auto'\n */\n readonly tone?: CurrencyTone\n /** The amount, in the currency's major units. */\n readonly value: number\n}\n\ntype CurrencySignDisplay = 'always' | 'auto' | 'never'\n\ntype CurrencyTone = 'auto' | 'negative' | 'neutral' | 'positive'\n\n/**\n * A formatter, how it writes zero, and the currency code written beside its\n * number when it is the decimal fallback for a code the runtime rejected.\n */\ntype ResolvedFormat = {\n readonly code: null | string\n readonly formatter: Intl.NumberFormat\n readonly zero: string\n}\n\n/**\n * An amount of money, formatted for the reader's locale in the mono face with\n * tabular figures, so a column of amounts lines up on the decimal point.\n * Negatives carry a true minus sign, and the tone follows the sign unless\n * `tone` names one.\n *\n * ```tsx\n * <Text variant=\"bodyLarge\">\n * <Currency currency=\"EUR\" value={-42.5} />\n * </Text>\n * ```\n *\n * It sets no size of its own, so it takes the type of the Text around it:\n * put it in one, a heading-scale one for a larger figure. On its own it draws\n * at the platform's default size. The face is the mono family's first, once\n * the app has registered it with expo-font, and the system font until then,\n * with tabular figures either way.\n *\n * Web's `render` has no counterpart. `style`, `ref` and `testID` land on React\n * Native's own Text.\n */\nfunction Currency({\n currency = 'USD',\n locale,\n sign = 'auto',\n style,\n tone = 'auto',\n value,\n ...props\n}: CurrencyProps) {\n const theme = useTheme()\n const own = stylesOf(theme)\n const family = familyStyle(useFontFamily(theme.typography.fontFamilyMono))\n\n const writtenIn = useLocaleOf(locale)\n const amount = formatCurrency(value, { currency, locale: writtenIn, sign })\n\n return (\n <NativeText\n {...props}\n style={composeStyles<TextStyle>(\n own.base,\n family,\n own[resolveTone(value, tone, amount.shownAsZero)],\n style,\n )}\n >\n {amount.text}\n </NativeText>\n )\n}\n\n// Constructing an Intl.NumberFormat resolves the locale, loads the currency's\n// data and builds the pattern; formatting with one already built skips all\n// three. An instance is stateless once constructed, so one can be shared\n// across every component asking for the same locale and currency. The sign\n// is no part of the key, since every setting is drawn from the one formatter.\n//\n// What this saves is a mount and a changing amount, not an idle re-render:\n// the React Compiler already memoises the call below on the things it reads,\n// so a component re-rendered with the same props never reaches here. That\n// cache belongs to one component instance and counts `value` among its keys,\n// which leaves two cases it cannot cover: a column of rows, where every\n// instance has a cache of its own and each builds a formatter, and a single\n// amount that ticks, where each new value misses.\n//\n// Unbounded on purpose. The keys are the pairs an app actually uses, its\n// locales times its currencies, so the map settles at that size rather than\n// growing with renders or with values.\n//\n// A request the runtime rejects is cached too, under its own key, as the\n// fallback it resolved to, so a bad pair costs one failed construction rather\n// than one a render.\nconst formatters = new Map<string, ResolvedFormat>()\n\n// The amount is read off `format` alone, where web reads it off\n// `formatToParts` with the `signDisplay` settings that leave a zero bare.\n// Hermes has neither: it defines no `formatToParts` on iOS, ignores\n// `signDisplay` there, and takes only `auto`, `never`, `always` and\n// `exceptZero` on Android, so the `negative` web asks for throws. `format` is\n// what every runtime React Native runs on has.\n//\n// So the magnitude is formatted on its own, and it shows as zero exactly when\n// it reads as zero formatted by the same formatter, whatever digits the\n// locale writes. A sign, where one is shown, goes where the locale puts its\n// own minus. On a runtime with `formatToParts` the text this writes is the\n// text web writes, which the tests compare across locales.\nfunction formatCurrency(\n value: number,\n options: {\n currency: string\n locale: string | undefined\n sign: CurrencySignDisplay\n },\n) {\n const { code, formatter, zero } = formatterFor(\n options.locale,\n options.currency,\n )\n const magnitude = formatter.format(Math.abs(value))\n // Whether every digit shown is a zero, which is what the tone reads rather\n // than the raw value: the two never saw the same number before.\n const shownAsZero = magnitude === zero\n const mark = signFor(value, options.sign, shownAsZero)\n const amount = mark === undefined ? magnitude : signed(formatter, value, mark)\n\n return {\n shownAsZero,\n text: code === null || code === '' ? amount : `${amount} ${code}`,\n }\n}\n\n// A component that only shows an amount must not take the screen down with\n// it, and `new Intl.NumberFormat` throws for a malformed locale (`xx_YY`) and\n// for a currency code that is not one (`xx`, `US`). Valid input takes exactly\n// the path it always did; the rest falls back. A locale the runtime rejects,\n// an empty one among them, which is what clearing a locale control leaves,\n// gives way to the device's, and a currency code it rejects to the plain\n// number in that locale with the code written after it, so the amount still\n// reads and what was wrong with it stays visible.\n//\n// Nothing is logged: the library writes to the console nowhere, and a\n// warning an app cannot turn off is not its to emit.\nfunction formatterFor(locale: string | undefined, currency: string) {\n // Neither a BCP 47 locale nor an ISO 4217 code can contain a vertical bar,\n // so joining on one cannot make two different pairs share a key.\n const key = `${locale ?? ''}|${currency}`\n const cached = formatters.get(key)\n if (cached !== undefined) {\n return cached\n }\n\n const usable = locale !== undefined && isLocale(locale) ? locale : undefined\n let formatter: Intl.NumberFormat\n let code: null | string = null\n try {\n formatter = new Intl.NumberFormat(usable, { currency, style: 'currency' })\n } catch {\n formatter = new Intl.NumberFormat(usable, {\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n })\n code = currency\n }\n\n const resolved = { code, formatter, zero: formatter.format(0) }\n formatters.set(key, resolved)\n return resolved\n}\n\n// Whether the runtime takes `tag` as a locale at all — one it parses, whether\n// or not it has data for it, which it then fills in from the nearest it has.\nfunction isLocale(tag: string) {\n try {\n return Intl.NumberFormat.supportedLocalesOf(tag).length >= 0\n } catch {\n return false\n }\n}\n\n// An amount shown as zero is neither owed nor owing, so it takes the neutral\n// role whatever the raw value's sign: negative zero, a rounded-away debt and\n// float residue all read as settled. Otherwise the sign decides.\nfunction resolveTone(value: number, tone: CurrencyTone, shownAsZero: boolean) {\n if (tone !== 'auto') {\n return tone\n }\n if (shownAsZero) {\n return 'neutral'\n }\n if (value > 0) {\n return 'positive'\n }\n if (value < 0) {\n return 'negative'\n }\n return 'neutral'\n}\n\n// The amount with `mark` in place of the minus the locale writes on a\n// negative, so the mark sits where the locale puts its own sign: before the\n// symbol or after it, beside a direction mark in a right-to-left locale, or in\n// place of a space, as Swiss German's `CHF-12.50` does beside `CHF 12.50`. A\n// plus takes the same place, which is where Intl's own `exceptZero` puts it.\n// Only the sign is replaced: nothing a locale writes ahead of it, a symbol, a\n// code, a space or a direction mark, is a minus, so the first is the sign.\nfunction signed(formatter: Intl.NumberFormat, value: number, mark: string) {\n return formatter.format(-Math.abs(value)).replace(LOCALE_MINUS, mark)\n}\n\n// The sign an amount carries, if any. One shown as zero carries none under\n// any setting, since there is nothing owed or owing; otherwise a negative\n// takes a minus unless told to show none, and a positive a plus only when\n// told to always show one.\nfunction signFor(\n value: number,\n sign: CurrencySignDisplay,\n shownAsZero: boolean,\n) {\n if (shownAsZero || sign === 'never') {\n return undefined\n }\n if (value < 0) {\n return MINUS_SIGN\n }\n if (value > 0 && sign === 'always') {\n return PLUS_SIGN\n }\n return undefined\n}\n\nexport type { CurrencyProps, CurrencySignDisplay, CurrencyTone }\n\nexport { Currency }\n"],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ButtonLinkForm, ButtonPressForm } from "../../button/index.js";
|
|
2
|
+
import { FabSize, FabTone, FabVariant } from "../../fab/index.js";
|
|
3
|
+
import { PressableProps, StyleProp, View, ViewStyle } from "react-native";
|
|
4
|
+
import { ComponentRef, ReactNode, Ref } from "react";
|
|
5
|
+
//#region src/components/fab/index.d.ts
|
|
6
|
+
type FabNaming = {
|
|
7
|
+
/**
|
|
8
|
+
* What the FAB does, in words. A FAB without a `label` is an icon
|
|
9
|
+
* alone, which has no name of its own, so it needs this; with a
|
|
10
|
+
* `label` the label is its name.
|
|
11
|
+
*/
|
|
12
|
+
readonly 'aria-label': string;
|
|
13
|
+
readonly label?: never;
|
|
14
|
+
} | {
|
|
15
|
+
readonly 'aria-label'?: string;
|
|
16
|
+
/**
|
|
17
|
+
* A label after the icon, which makes this the extended FAB: as tall
|
|
18
|
+
* as the size, as wide as what it holds.
|
|
19
|
+
*/
|
|
20
|
+
readonly label: ReactNode;
|
|
21
|
+
};
|
|
22
|
+
type FabPressForm = Omit<ButtonPressForm, 'isPending' | 'pendingLabel'> & {
|
|
23
|
+
readonly isPending?: never;
|
|
24
|
+
readonly pendingLabel?: never;
|
|
25
|
+
};
|
|
26
|
+
type FabProps = (ButtonLinkForm | FabPressForm) & FabNaming & Omit<PressableProps, 'aria-label' | 'children' | 'disabled' | 'style'> & {
|
|
27
|
+
/**
|
|
28
|
+
* The FAB's icon. The FAB hands it its colour and the size's icon size
|
|
29
|
+
* through `useIconStyle()`.
|
|
30
|
+
*/
|
|
31
|
+
readonly children?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Draws no press ripple. The state layers are unaffected.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
readonly disableRipple?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Draws the FAB disabled, without its shadow, and takes no presses.
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
readonly isDisabled?: boolean;
|
|
42
|
+
readonly ref?: Ref<ComponentRef<typeof View>>;
|
|
43
|
+
/**
|
|
44
|
+
* The pages' three sizes: `md` the FAB at 56dp, `lg` the medium FAB at
|
|
45
|
+
* 80dp and `xl` the large FAB at 96dp.
|
|
46
|
+
* @default 'md'
|
|
47
|
+
*/
|
|
48
|
+
readonly size?: FabSize;
|
|
49
|
+
readonly style?: StyleProp<ViewStyle>;
|
|
50
|
+
/**
|
|
51
|
+
* The colour role the FAB is drawn in.
|
|
52
|
+
* @default 'primary'
|
|
53
|
+
*/
|
|
54
|
+
readonly tone?: FabTone;
|
|
55
|
+
/**
|
|
56
|
+
* `tonal` draws the tone's container pair, the pages' default; `filled`
|
|
57
|
+
* draws the tone itself.
|
|
58
|
+
* @default 'tonal'
|
|
59
|
+
*/
|
|
60
|
+
readonly variant?: FabVariant;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A screen's primary action, raised over its content. Its icon is its
|
|
64
|
+
* children; given a `label` it is the extended FAB, with the label after the
|
|
65
|
+
* icon. Given `href` it is a link with the same appearance.
|
|
66
|
+
*
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <Fab aria-label="Label">
|
|
69
|
+
* <PlusIcon />
|
|
70
|
+
* </Fab>
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* Without a `label` it is an icon alone, so `aria-label` is required there.
|
|
74
|
+
* `style`, `ref` and `testID` land on the `Pressable`, which is what a call
|
|
75
|
+
* site positions over its screen.
|
|
76
|
+
*/
|
|
77
|
+
declare function Fab({ children, delayLongPress, disableRipple, href, isDisabled, label, onBlur, onFocus, onHoverIn, onHoverOut, onLongPress, onPress, onPressIn, onPressOut, ref, size, slot, style, tone, type, variant, ...props }: FabProps): import("react").JSX.Element;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { Fab, type FabProps, type FabSize, type FabTone, type FabVariant };
|
|
80
|
+
//# sourceMappingURL=index.d.ts.map
|