@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ComponentRef","ReactNode","Ref","LayoutChangeEvent","StyleProp","TextStyle","ViewProps","ViewStyle","AnimatedStyle","SharedValue","use","useEffect","useRef","useState","useSyncExternalStore","AccessibilityInfo","Text","NativeText","Platform","Pressable","StyleSheet","View","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","SafeAreaInsetsContext","CloseGlyph","useWindowSizeClass","Overlay","hitSlop","usePressFeedback","useTheme","composeStyles","themedStyles","useTypeRole","MESSAGE_MS","UNTIL_CLOSED","MIN_WIDE","MAX_WIDE","MESSAGE_BLOCK_PADDING","RING_WIDTH","RING_OFFSET","NO_INSETS","bottom","left","right","top","stylesOf","colors","radii","shadows","sizing","spacing","create","action","alignItems","justifyContent","minHeight","controlSm","paddingHorizontal","md","paddingVertical","sm","actionLabel","color","inversePrimary","actionRing","outlineColor","outlineOffset","outlineStyle","outlineWidth","close","height","controlMd","width","closeRing","inverseOnSurface","control","borderRadius","pill","message","flexBasis","flexGrow","marginHorizontal","minWidth","messageText","region","paddingTop","pointerEvents","position","strip","backgroundColor","inverseSurface","xs","boxShadow","elevation3","flexDirection","flexWrap","styles","controls","flexShrink","marginStart","wide","maxWidth","ShownMessage","SnackbarAction","key","onClose","showCloseButton","timeout","label","onPress","SnackbarOptions","SnackbarProps","Omit","closeLabel","queue","SnackbarQueue","ref","style","added","listeners","Set","shown","add","options","clear","String","undefined","changed","subscribe","listener","delete","visible","#changed","actionOf","closeOf","flagOf","set","value","measureInto","event","nativeEvent","layout","Snackbar","t0","$","_c","props","t1","theme","insets","t2","standing","t3","paddingBottom","paddingLeft","paddingRight","t4","t5","OS","SnackbarButton","ariaLabel","children","onFocusChange","ring","corners","feedback","onBlur","onFocus","T0","handlers","t6","t7","layer","SnackbarStrip","arrives","themed","body","reduceMotion","focused","setFocused","hovered","setHovered","motion","rises","arrived","rise","progress","get","opacity","transform","translateY","duration","durationMedium1","easing","bezier","easingEmphasizedDecelerate","announceForAccessibilityWithOptions","useExpiry","useShownFor","t8","Symbol","for","paused","expire","remaining","current","started","Date","now","timer","setTimeout","clearTimeout","recommended","setRecommended","asks","ask","milliseconds","getRecommendedTimeoutMillis","Queue","_c","NativeText"],"sources":["../../../src/components/snackbar/index.tsx"],"sourcesContent":["import type { ComponentRef, ReactNode, Ref } from 'react'\nimport type {\n LayoutChangeEvent,\n StyleProp,\n TextStyle,\n ViewProps,\n ViewStyle,\n} from 'react-native'\nimport type { AnimatedStyle, SharedValue } from 'react-native-reanimated'\n\nimport { use, useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport {\n AccessibilityInfo,\n Text as NativeText,\n Platform,\n Pressable,\n StyleSheet,\n View,\n} from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\nimport { SafeAreaInsetsContext } from 'react-native-safe-area-context'\n\nimport { CloseGlyph } from '../../glyphs'\nimport { useWindowSizeClass } from '../../hooks/useWindowSizeClass'\nimport { Overlay } from '../../overlay'\nimport { hitSlop, usePressFeedback } from '../../press'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useTypeRole } from '../../typography/roles'\n\n// The snackbar page's brief message at the bottom of the screen, with an\n// optional action beside it and an optional close button after that, as\n// kanso-ui's Snackbar draws it. The page's container is the inverse surface\n// at a 4dp corner and elevation 3, its supporting text is body-medium in\n// inverse on surface, its action label is inverse primary, and its close icon\n// takes the supporting text's colour. The measurements are the page's too: 8dp\n// of margin from the edges of the screen, 8dp of padding inside the container\n// with another 8dp either side of the message, 14dp above and below on a\n// single line, and between 320 and 576dp wide from the 600dp breakpoint up,\n// the width of the window less its margins below it. The margin is counted\n// from the safe area, so a message clears the home indicator and the gesture\n// bar.\n//\n// One at a time, as on web. The page shows a single snackbar and replaces it\n// rather than stacking, so a message added while another is up closes that\n// one, whose `onClose` runs, and takes its place. Nothing waits behind it, so\n// an Undo never comes back long after the thing it undid.\n//\n// The queue is web's API: `add` a message with its options from anywhere, and\n// mount one `Snackbar` with the queue near the root. It draws in the overlay\n// host of the outermost `ThemeScope`, so it floats above every screen in the\n// theme around it.\n//\n// **A message is read out as it arrives.** On iOS and Android it is\n// announced through `AccessibilityInfo`, after whatever VoiceOver is already\n// saying, which is how CopyField says its confirmation too. Under React Native\n// Web the region it appears in is a polite live region instead, as web's is,\n// and the browser's screen reader reads it as it changes. Android is not given\n// a live region as well, since TalkBack would then read each message twice.\n//\n// **A message is a string, where web takes a node.** The announcement is the\n// message's words, and a node has none to hand VoiceOver.\n//\n// **The action and the close button are drawn here rather than through\n// Button and IconButton**, as on web: the page colours them inverse primary\n// and inverse on surface, which no variant of either carries. They take the\n// state layer, the ripple and the focus ring from src/press, with the ring\n// drawn 2dp inside the control rather than outside it: the close button is\n// 48dp in a strip 48dp tall, so a ring outside it crossed the strip's edges\n// onto the screen, in a colour chosen to read on the strip rather than on\n// the screen around it.\n\n// How long a message is shown when the call site names no `timeout`, web's\n// durations from the snackbar's guidelines and accessibility pages. A message\n// alone leaves after five seconds, which Android lengthens from the reader's\n// own accessibility setting, as its own snackbar does. A message with an\n// action stays until the action is taken or something closes it, since no\n// timer knows how long a reader needs to reach it.\nconst MESSAGE_MS = 5000\nconst UNTIL_CLOSED = 0\n\n// The width from the 600dp breakpoint up, the page's. Literals, as on web:\n// they bound the strip rather than measure anything a theme would resize.\nconst MIN_WIDE = 320\nconst MAX_WIDE = 576\n\n// The page's 14dp above and below the message, which with its 20dp line makes\n// a single-line strip 48dp tall. A literal, as on web, since no spacing step\n// is 14.\nconst MESSAGE_BLOCK_PADDING = 14\n\n// The ring's 2dp, drawn 4dp in from the control's edge so all of it lies\n// inside.\nconst RING_WIDTH = 2\nconst RING_OFFSET = -4\n\n// Outside a SafeAreaProvider the insets are unknown, and the window's own edge\n// is the best bound there is.\nconst NO_INSETS = { bottom: 0, left: 0, right: 0, top: 0 }\n\nconst stylesOf = themedStyles(({ colors, radii, shadows, sizing, spacing }) =>\n StyleSheet.create({\n // Material Design's text button in the page's inverse primary, at the\n // label large the page gives it: 40dp tall, as Button's medium size is,\n // over a 48dp target, which is the strip's own height. A least height\n // rather than a height, so a type scale with a taller line grows the\n // button instead of clipping its label.\n action: {\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: sizing.controlSm,\n paddingHorizontal: spacing.md,\n paddingVertical: spacing.sm,\n },\n actionLabel: {\n color: colors.inversePrimary,\n },\n actionRing: {\n outlineColor: colors.inversePrimary,\n outlineOffset: RING_OFFSET,\n outlineStyle: 'solid',\n outlineWidth: RING_WIDTH,\n },\n // The page's 24dp icon in the supporting text's colour, in a 48dp box,\n // the least press area the icon buttons page allows.\n close: {\n alignItems: 'center',\n height: sizing.controlMd,\n justifyContent: 'center',\n width: sizing.controlMd,\n },\n closeRing: {\n outlineColor: colors.inverseOnSurface,\n outlineOffset: RING_OFFSET,\n outlineStyle: 'solid',\n outlineWidth: RING_WIDTH,\n },\n // Both controls are pills, which the state layer and the ripple clip to.\n control: {\n borderRadius: radii.pill,\n },\n // The message: its 8dp either side inside the strip's own 8dp, and the\n // 14dp above and below. The height is the message's rather than the\n // strip's, which is what lets a 48dp close button sit beside it without\n // making the strip taller than the page draws it.\n //\n // Its basis is half the strip, which is what decides when the controls\n // drop beneath it. A word longer than the line holds breaks inside\n // itself rather than running under the controls, which the least width\n // of 0 allows on the web, where a flex item otherwise keeps the width of\n // its longest word.\n message: {\n flexBasis: '50%',\n flexGrow: 1,\n marginHorizontal: spacing.sm,\n minWidth: 0,\n paddingVertical: MESSAGE_BLOCK_PADDING,\n },\n messageText: {\n color: colors.inverseOnSurface,\n },\n // A strip along the bottom of the overlay layer, with the page's 8dp\n // above it. It passes a touch through wherever no strip is drawn, so\n // the screen under it stays usable.\n region: {\n alignItems: 'center',\n bottom: 0,\n left: 0,\n paddingTop: spacing.sm,\n pointerEvents: 'box-none',\n position: 'absolute',\n right: 0,\n },\n // The container: the page's 8dp either side, and nothing above or\n // below, since what sets its height is the message and the close\n // button. It wraps, so controls too long to leave the message half the\n // strip drop onto a line of their own beneath it, Material Design's\n // two-line snackbar with a longer action.\n strip: {\n alignItems: 'center',\n backgroundColor: colors.inverseSurface,\n borderRadius: radii.xs,\n boxShadow: shadows.elevation3,\n flexDirection: 'row',\n flexWrap: 'wrap',\n minHeight: sizing.controlMd,\n paddingHorizontal: spacing.sm,\n width: '100%',\n },\n }),\n)\n\nconst styles = StyleSheet.create({\n // The action, and the close button after it, kept together after the\n // message so they move beneath it together. The auto margin puts them at\n // the trailing edge: on the message's row the message takes all the room\n // first, and on a row of their own it is all the room there is.\n controls: {\n alignItems: 'center',\n flexDirection: 'row',\n flexShrink: 0,\n marginStart: 'auto',\n },\n wide: {\n maxWidth: MAX_WIDE,\n minWidth: MIN_WIDE,\n },\n})\n\n// A message on screen, with what it was added with.\ntype ShownMessage = {\n readonly action: SnackbarAction | undefined\n readonly key: string\n readonly message: string\n readonly onClose: (() => void) | undefined\n readonly showCloseButton: boolean\n readonly timeout: number\n}\n\ntype SnackbarAction = {\n /** What the action says. The page gives it a single word where it can. */\n readonly label: string\n /** Run when the action is pressed, just before the snackbar closes. */\n readonly onPress: () => void\n}\n\ntype SnackbarOptions = {\n /**\n * A single action shown after the message. Pressing it runs `onPress` and\n * closes the snackbar, which is what the page draws.\n */\n readonly action?: SnackbarAction\n /**\n * Run when the snackbar closes, however it was closed: by its timer, its\n * action or close button, `close`, `clear`, or a newer message added in its\n * place.\n */\n readonly onClose?: () => void\n /**\n * Whether a close button is shown at the end. The page draws one for a\n * message a reader may want out of the way before it expires.\n * @default false\n */\n readonly showCloseButton?: boolean\n /**\n * How long the snackbar is shown, in milliseconds. Defaults to five seconds\n * for a message alone, which Android lengthens from the reader's own\n * accessibility setting. A message with an action defaults to `0` and stays\n * until the action is taken or something closes it, as Material Design asks\n * of an actionable snackbar. The timer is paused while a pointer is over the\n * snackbar or one of its controls has focus, so a message never expires\n * while it is being read.\n *\n * `0` is the page's indefinite length: the snackbar stays until something\n * closes it. Give one of those an action or `showCloseButton`, or there is\n * no way to be rid of it.\n */\n readonly timeout?: number\n}\n\ntype SnackbarProps = Omit<ViewProps, 'children' | 'style'> & {\n /**\n * What a screen reader calls the button that dismisses a snackbar. It sits\n * here rather than on a message's own options because it does not vary by\n * message.\n * @default 'Close'\n */\n readonly closeLabel?: string\n /** The queue the snackbar shows messages from. */\n readonly queue: SnackbarQueue\n readonly ref?: Ref<ComponentRef<typeof View>>\n readonly style?: StyleProp<ViewStyle>\n}\n\n/**\n * The queue a snackbar is shown from. Make one where the app can reach it, a\n * module of its own being the usual place, mount `Snackbar` once with it, and\n * call `add` from anywhere.\n *\n * ```ts\n * const messages = new Snackbar.Queue()\n * messages.add('Label', { action: { label: 'Undo', onPress: undo } })\n * ```\n */\nclass SnackbarQueue {\n #added = 0\n readonly #listeners = new Set<() => void>()\n #shown: null | ShownMessage = null\n\n /**\n * Shows a message in place of the one on screen, and returns the key that\n * closes it. The message it replaces is closed, so its `onClose` runs, and\n * it does not come back.\n */\n add(message: string, options: SnackbarOptions = {}): string {\n const { action, onClose, showCloseButton = false, timeout } = options\n\n this.clear()\n this.#added += 1\n\n const key = `snackbar-${String(this.#added)}`\n\n this.#shown = {\n action,\n key,\n message,\n onClose,\n showCloseButton,\n timeout: timeout ?? (action === undefined ? MESSAGE_MS : UNTIL_CLOSED),\n }\n this.#changed()\n\n return key\n }\n\n /** Closes the snackbar on screen, running its `onClose`. */\n clear() {\n if (this.#shown !== null) {\n this.close(this.#shown.key)\n }\n }\n\n /** Closes one snackbar, by the key `add` returned. */\n close(key: string) {\n const shown = this.#shown\n\n if (shown?.key !== key) {\n return\n }\n\n // Off the screen before `onClose` runs, so one that adds the next message\n // adds it to an empty queue.\n this.#shown = null\n this.#changed()\n shown.onClose?.()\n }\n\n /**\n * Calls `listener` whenever the message on screen changes, and returns what\n * stops it. `Snackbar` reads the queue through this and `visible`, and\n * neither is meant for a call site.\n */\n readonly subscribe = (listener: () => void) => {\n this.#listeners.add(listener)\n\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** The message on screen, or `null`. */\n readonly visible = () => this.#shown\n\n #changed() {\n for (const listener of this.#listeners) {\n listener()\n }\n }\n}\n\n// The handlers the strip hands its views, each built by a call rather than\n// declared in the component, which is what react-perf's\n// jsx-no-new-function-as-prop is after; the React Compiler memoises the result\n// on its inputs.\nfunction actionOf(action: SnackbarAction, close: () => void) {\n return () => {\n action.onPress()\n close()\n }\n}\n\nfunction closeOf(queue: SnackbarQueue, key: string) {\n return () => {\n queue.close(key)\n }\n}\n\nfunction flagOf(set: (value: boolean) => void, value: boolean) {\n return () => {\n set(value)\n }\n}\n\nfunction measureInto(height: SharedValue<number>) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n }\n}\n\n/**\n * A brief message at the bottom of the screen, with an optional action, with\n * web's props and values. Mount one of these with a `Snackbar.Queue`, and\n * call the queue's `add` to show a message from anywhere.\n *\n * ```tsx\n * const messages = new Snackbar.Queue()\n *\n * <Snackbar queue={messages} />\n * ```\n *\n * It draws in the overlay host of the app's outermost `ThemeScope`, above the\n * screen, and needs one around it. It is one region rather than one per\n * message, so mount it once, near the root, inside the `SafeAreaProvider`\n * whose insets it keeps clear of.\n *\n * `style`, `ref` and `testID` land on that region, a view along the bottom of\n * the screen that holds the message.\n */\nfunction Snackbar({\n closeLabel = 'Close',\n queue,\n style,\n ...props\n}: SnackbarProps) {\n const theme = useTheme()\n const insets = use(SafeAreaInsetsContext) ?? NO_INSETS\n const wide = useWindowSizeClass() !== 'compact'\n const shown = useSyncExternalStore(queue.subscribe, queue.visible)\n // The message up when the snackbar mounted, which was on screen before it\n // rather than arriving, so it is put in place rather than risen to it: on\n // a screen that remounts the snackbar, and on a story seeded with one.\n const [standing] = useState(() => queue.visible()?.key)\n const { spacing } = theme\n\n return (\n <Overlay>\n <View\n aria-live={Platform.OS === 'web' ? 'polite' : undefined}\n {...props}\n style={composeStyles<ViewStyle>(\n stylesOf(theme).region,\n {\n paddingBottom: spacing.sm + insets.bottom,\n paddingLeft: spacing.sm + insets.left,\n paddingRight: spacing.sm + insets.right,\n },\n style,\n )}\n >\n {shown === null ? null : (\n <SnackbarStrip\n arrives={shown.key !== standing}\n closeLabel={closeLabel}\n key={shown.key}\n queue={queue}\n shown={shown}\n wide={wide}\n />\n )}\n </View>\n </Overlay>\n )\n}\n\n/**\n * One of the snackbar's two controls, a pill drawing the state layer, the\n * ripple and the ring in its own colour, over a target brought up to 48dp\n * from the `height` it is drawn at. It reports when it takes and loses focus,\n * which pauses the message's timer.\n */\nfunction SnackbarButton({\n 'aria-label': ariaLabel,\n children,\n color,\n height,\n onFocusChange,\n onPress,\n ring,\n style,\n}: {\n readonly 'aria-label'?: string\n readonly children: ReactNode\n readonly color: string\n readonly height: number\n readonly onFocusChange: (focused: boolean) => void\n readonly onPress: () => void\n readonly ring: ViewStyle\n readonly style: ViewStyle\n}) {\n const theme = useTheme()\n const corners = stylesOf(theme).control\n const feedback = usePressFeedback({\n color,\n corners,\n onBlur: () => {\n onFocusChange(false)\n },\n onFocus: () => {\n onFocusChange(true)\n },\n })\n\n return (\n <Pressable\n {...feedback.handlers}\n aria-label={ariaLabel}\n hitSlop={hitSlop(height)}\n onPress={onPress}\n role=\"button\"\n style={composeStyles<ViewStyle>(\n corners,\n style,\n feedback.ring === undefined ? undefined : ring,\n )}\n >\n {feedback.layer}\n {children}\n </Pressable>\n )\n}\n\n/** One message: the words, then whatever sits after them. */\nfunction SnackbarStrip({\n arrives,\n closeLabel,\n queue,\n shown,\n wide,\n}: {\n /** Whether the message rises into place, or is drawn there at once. */\n readonly arrives: boolean\n readonly closeLabel: string\n readonly queue: SnackbarQueue\n readonly shown: ShownMessage\n readonly wide: boolean\n}) {\n const theme = useTheme()\n const themed = stylesOf(theme)\n const body = useTypeRole('bodyMedium')\n const label = useTypeRole('labelLarge')\n const reduceMotion = useReducedMotion()\n const [focused, setFocused] = useState(false)\n const [hovered, setHovered] = useState(false)\n const { action, key, message, showCloseButton } = shown\n const { colors, motion, sizing } = theme\n\n // It rises its own height from the bottom edge as it fades in, which is\n // what the page draws and what says the message came from the edge of the\n // screen rather than appearing over it. Reduced motion puts it in its\n // settled place at once, as web's zeroed duration does.\n const rises = arrives && !reduceMotion\n const arrived = useSharedValue(rises ? 0 : 1)\n const height = useSharedValue(0)\n const rise = useAnimatedStyle(() => {\n 'worklet'\n const progress = arrived.get()\n\n return {\n opacity: progress,\n transform: [{ translateY: (1 - progress) * height.get() }],\n }\n })\n\n useEffect(() => {\n if (rises) {\n arrived.set(\n withTiming(1, {\n duration: motion.durationMedium1,\n easing: Easing.bezier(...motion.easingEmphasizedDecelerate),\n }),\n )\n }\n }, [arrived, motion, rises])\n\n useEffect(() => {\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n AccessibilityInfo.announceForAccessibilityWithOptions(message, {\n queue: true,\n })\n }\n }, [message])\n\n const close = closeOf(queue, key)\n\n useExpiry(useShownFor(shown.timeout), focused || hovered, close)\n\n return (\n <Animated.View\n onLayout={measureInto(height)}\n onPointerEnter={flagOf(setHovered, true)}\n onPointerLeave={flagOf(setHovered, false)}\n style={composeStyles<AnimatedStyle<ViewStyle>>(\n themed.strip,\n wide ? styles.wide : undefined,\n rise,\n )}\n >\n <View style={themed.message}>\n <NativeText style={composeStyles<TextStyle>(body, themed.messageText)}>\n {message}\n </NativeText>\n </View>\n {action === undefined && !showCloseButton ? null : (\n <View style={styles.controls}>\n {action === undefined ? null : (\n <SnackbarButton\n color={colors.inversePrimary}\n height={sizing.controlSm}\n onFocusChange={setFocused}\n onPress={actionOf(action, close)}\n ring={themed.actionRing}\n style={themed.action}\n >\n <NativeText\n style={composeStyles<TextStyle>(label, themed.actionLabel)}\n >\n {action.label}\n </NativeText>\n </SnackbarButton>\n )}\n {showCloseButton ? (\n <SnackbarButton\n aria-label={closeLabel}\n color={colors.inverseOnSurface}\n height={sizing.controlMd}\n onFocusChange={setFocused}\n onPress={close}\n ring={themed.closeRing}\n style={themed.close}\n >\n <CloseGlyph color={colors.inverseOnSurface} size={24} />\n </SnackbarButton>\n ) : null}\n </View>\n )}\n </Animated.View>\n )\n}\n\n// Closes a message once it has been shown for `duration` milliseconds,\n// counting only the time it was not paused. Nothing for a `duration` of 0,\n// which is until closed, or of `null`, which is not yet known.\nfunction useExpiry(\n duration: null | number,\n paused: boolean,\n expire: () => void,\n) {\n const left = useRef<null | number>(null)\n\n useEffect(() => {\n if (duration === null || duration === 0 || paused) {\n return undefined\n }\n\n const remaining = left.current ?? duration\n const started = Date.now()\n const timer = setTimeout(expire, remaining)\n\n return () => {\n clearTimeout(timer)\n left.current = remaining - (Date.now() - started)\n }\n }, [duration, expire, paused])\n}\n\n// How long a message is shown: its `timeout`, which Android lengthens from\n// the reader's \"Time to take action\" setting, as its own snackbar does, and\n// is `null` until Android has answered. Nothing is asked for a message shown\n// until closed.\nfunction useShownFor(timeout: number): null | number {\n const [recommended, setRecommended] = useState<null | number>(null)\n const asks = Platform.OS === 'android' && timeout > 0\n\n useEffect(() => {\n if (!asks) {\n return undefined\n }\n\n let current = true\n\n async function ask() {\n const milliseconds =\n await AccessibilityInfo.getRecommendedTimeoutMillis(timeout)\n\n if (current) {\n setRecommended(milliseconds)\n }\n }\n\n void ask()\n\n return () => {\n current = false\n }\n }, [asks, timeout])\n\n return asks ? recommended : timeout\n}\n\nSnackbar.Queue = SnackbarQueue\n\n// The queue is reached as `Snackbar.Queue` rather than exported beside the\n// component, as on web: a module exporting something that is not a component\n// is one fast refresh reloads whole.\nexport { Snackbar }\nexport type { SnackbarAction, SnackbarOptions, SnackbarProps, SnackbarQueue }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ComponentRef","ReactNode","Ref","LayoutChangeEvent","StyleProp","TextStyle","ViewProps","ViewStyle","AnimatedStyle","SharedValue","use","useEffect","useRef","useState","useSyncExternalStore","AccessibilityInfo","Text","NativeText","Platform","Pressable","StyleSheet","View","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","SafeAreaInsetsContext","CloseGlyph","useWindowSizeClass","useMessages","Overlay","hitSlop","usePressFeedback","useTheme","composeStyles","themedStyles","useTypeRole","MESSAGE_MS","UNTIL_CLOSED","MIN_WIDE","MAX_WIDE","MESSAGE_BLOCK_PADDING","RING_WIDTH","RING_OFFSET","NO_INSETS","bottom","left","right","top","stylesOf","colors","radii","shadows","sizing","spacing","create","action","alignItems","justifyContent","minHeight","controlSm","paddingHorizontal","md","paddingVertical","sm","actionLabel","color","inversePrimary","actionRing","outlineColor","outlineOffset","outlineStyle","outlineWidth","close","height","controlMd","width","closeRing","inverseOnSurface","control","borderRadius","pill","message","flexBasis","flexGrow","marginHorizontal","minWidth","messageText","region","paddingTop","pointerEvents","position","strip","backgroundColor","inverseSurface","xs","boxShadow","elevation3","flexDirection","flexWrap","styles","controls","flexShrink","marginStart","wide","maxWidth","ShownMessage","SnackbarAction","key","onClose","showCloseButton","timeout","label","onPress","SnackbarOptions","SnackbarProps","Omit","closeLabel","queue","SnackbarQueue","ref","style","added","listeners","Set","shown","add","options","clear","String","undefined","changed","subscribe","listener","delete","visible","#changed","actionOf","closeOf","flagOf","set","value","measureInto","event","nativeEvent","layout","Snackbar","t0","$","_c","ownCloseLabel","props","messages","theme","insets","t1","standing","t2","paddingBottom","paddingLeft","paddingRight","t3","t4","OS","SnackbarButton","ariaLabel","children","onFocusChange","ring","corners","feedback","onBlur","onFocus","T0","handlers","t5","t6","t7","layer","SnackbarStrip","arrives","themed","body","reduceMotion","focused","setFocused","hovered","setHovered","motion","rises","arrived","rise","progress","get","opacity","transform","translateY","duration","durationMedium1","easing","bezier","easingEmphasizedDecelerate","announceForAccessibilityWithOptions","useExpiry","useShownFor","t8","Symbol","for","paused","expire","remaining","current","started","Date","now","timer","setTimeout","clearTimeout","recommended","setRecommended","asks","ask","milliseconds","getRecommendedTimeoutMillis","Queue","_c","NativeText"],"sources":["../../../src/components/snackbar/index.tsx"],"sourcesContent":["import type { ComponentRef, ReactNode, Ref } from 'react'\nimport type {\n LayoutChangeEvent,\n StyleProp,\n TextStyle,\n ViewProps,\n ViewStyle,\n} from 'react-native'\nimport type { AnimatedStyle, SharedValue } from 'react-native-reanimated'\n\nimport { use, useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport {\n AccessibilityInfo,\n Text as NativeText,\n Platform,\n Pressable,\n StyleSheet,\n View,\n} from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\nimport { SafeAreaInsetsContext } from 'react-native-safe-area-context'\n\nimport { CloseGlyph } from '../../glyphs'\nimport { useWindowSizeClass } from '../../hooks/useWindowSizeClass'\nimport { useMessages } from '../../i18n/provider'\nimport { Overlay } from '../../overlay'\nimport { hitSlop, usePressFeedback } from '../../press'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\nimport { useTypeRole } from '../../typography/roles'\n\n// The snackbar page's brief message at the bottom of the screen, with an\n// optional action beside it and an optional close button after that, as\n// kanso-ui's Snackbar draws it. The page's container is the inverse surface\n// at a 4dp corner and elevation 3, its supporting text is body-medium in\n// inverse on surface, its action label is inverse primary, and its close icon\n// takes the supporting text's colour. The measurements are the page's too: 8dp\n// of margin from the edges of the screen, 8dp of padding inside the container\n// with another 8dp either side of the message, 14dp above and below on a\n// single line, and between 320 and 576dp wide from the 600dp breakpoint up,\n// the width of the window less its margins below it. The margin is counted\n// from the safe area, so a message clears the home indicator and the gesture\n// bar.\n//\n// One at a time, as on web. The page shows a single snackbar and replaces it\n// rather than stacking, so a message added while another is up closes that\n// one, whose `onClose` runs, and takes its place. Nothing waits behind it, so\n// an Undo never comes back long after the thing it undid.\n//\n// The queue is web's API: `add` a message with its options from anywhere, and\n// mount one `Snackbar` with the queue near the root. It draws in the overlay\n// host of the outermost `ThemeScope`, so it floats above every screen in the\n// theme around it.\n//\n// **A message is read out as it arrives.** On iOS and Android it is\n// announced through `AccessibilityInfo`, after whatever VoiceOver is already\n// saying, which is how CopyField says its confirmation too. Under React Native\n// Web the region it appears in is a polite live region instead, as web's is,\n// and the browser's screen reader reads it as it changes. Android is not given\n// a live region as well, since TalkBack would then read each message twice.\n//\n// **A message is a string, where web takes a node.** The announcement is the\n// message's words, and a node has none to hand VoiceOver.\n//\n// **The action and the close button are drawn here rather than through\n// Button and IconButton**, as on web: the page colours them inverse primary\n// and inverse on surface, which no variant of either carries. They take the\n// state layer, the ripple and the focus ring from src/press, with the ring\n// drawn 2dp inside the control rather than outside it: the close button is\n// 48dp in a strip 48dp tall, so a ring outside it crossed the strip's edges\n// onto the screen, in a colour chosen to read on the strip rather than on\n// the screen around it.\n\n// How long a message is shown when the call site names no `timeout`, web's\n// durations from the snackbar's guidelines and accessibility pages. A message\n// alone leaves after five seconds, which Android lengthens from the reader's\n// own accessibility setting, as its own snackbar does. A message with an\n// action stays until the action is taken or something closes it, since no\n// timer knows how long a reader needs to reach it.\nconst MESSAGE_MS = 5000\nconst UNTIL_CLOSED = 0\n\n// The width from the 600dp breakpoint up, the page's. Literals, as on web:\n// they bound the strip rather than measure anything a theme would resize.\nconst MIN_WIDE = 320\nconst MAX_WIDE = 576\n\n// The page's 14dp above and below the message, which with its 20dp line makes\n// a single-line strip 48dp tall. A literal, as on web, since no spacing step\n// is 14.\nconst MESSAGE_BLOCK_PADDING = 14\n\n// The ring's 2dp, drawn 4dp in from the control's edge so all of it lies\n// inside.\nconst RING_WIDTH = 2\nconst RING_OFFSET = -4\n\n// Outside a SafeAreaProvider the insets are unknown, and the window's own edge\n// is the best bound there is.\nconst NO_INSETS = { bottom: 0, left: 0, right: 0, top: 0 }\n\nconst stylesOf = themedStyles(({ colors, radii, shadows, sizing, spacing }) =>\n StyleSheet.create({\n // Material Design's text button in the page's inverse primary, at the\n // label large the page gives it: 40dp tall, as Button's medium size is,\n // over a 48dp target, which is the strip's own height. A least height\n // rather than a height, so a type scale with a taller line grows the\n // button instead of clipping its label.\n action: {\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: sizing.controlSm,\n paddingHorizontal: spacing.md,\n paddingVertical: spacing.sm,\n },\n actionLabel: {\n color: colors.inversePrimary,\n },\n actionRing: {\n outlineColor: colors.inversePrimary,\n outlineOffset: RING_OFFSET,\n outlineStyle: 'solid',\n outlineWidth: RING_WIDTH,\n },\n // The page's 24dp icon in the supporting text's colour, in a 48dp box,\n // the least press area the icon buttons page allows.\n close: {\n alignItems: 'center',\n height: sizing.controlMd,\n justifyContent: 'center',\n width: sizing.controlMd,\n },\n closeRing: {\n outlineColor: colors.inverseOnSurface,\n outlineOffset: RING_OFFSET,\n outlineStyle: 'solid',\n outlineWidth: RING_WIDTH,\n },\n // Both controls are pills, which the state layer and the ripple clip to.\n control: {\n borderRadius: radii.pill,\n },\n // The message: its 8dp either side inside the strip's own 8dp, and the\n // 14dp above and below. The height is the message's rather than the\n // strip's, which is what lets a 48dp close button sit beside it without\n // making the strip taller than the page draws it.\n //\n // Its basis is half the strip, which is what decides when the controls\n // drop beneath it. A word longer than the line holds breaks inside\n // itself rather than running under the controls, which the least width\n // of 0 allows on the web, where a flex item otherwise keeps the width of\n // its longest word.\n message: {\n flexBasis: '50%',\n flexGrow: 1,\n marginHorizontal: spacing.sm,\n minWidth: 0,\n paddingVertical: MESSAGE_BLOCK_PADDING,\n },\n messageText: {\n color: colors.inverseOnSurface,\n },\n // A strip along the bottom of the overlay layer, with the page's 8dp\n // above it. It passes a touch through wherever no strip is drawn, so\n // the screen under it stays usable.\n region: {\n alignItems: 'center',\n bottom: 0,\n left: 0,\n paddingTop: spacing.sm,\n pointerEvents: 'box-none',\n position: 'absolute',\n right: 0,\n },\n // The container: the page's 8dp either side, and nothing above or\n // below, since what sets its height is the message and the close\n // button. It wraps, so controls too long to leave the message half the\n // strip drop onto a line of their own beneath it, Material Design's\n // two-line snackbar with a longer action.\n strip: {\n alignItems: 'center',\n backgroundColor: colors.inverseSurface,\n borderRadius: radii.xs,\n boxShadow: shadows.elevation3,\n flexDirection: 'row',\n flexWrap: 'wrap',\n minHeight: sizing.controlMd,\n paddingHorizontal: spacing.sm,\n width: '100%',\n },\n }),\n)\n\nconst styles = StyleSheet.create({\n // The action, and the close button after it, kept together after the\n // message so they move beneath it together. The auto margin puts them at\n // the trailing edge: on the message's row the message takes all the room\n // first, and on a row of their own it is all the room there is.\n controls: {\n alignItems: 'center',\n flexDirection: 'row',\n flexShrink: 0,\n marginStart: 'auto',\n },\n wide: {\n maxWidth: MAX_WIDE,\n minWidth: MIN_WIDE,\n },\n})\n\n// A message on screen, with what it was added with.\ntype ShownMessage = {\n readonly action: SnackbarAction | undefined\n readonly key: string\n readonly message: string\n readonly onClose: (() => void) | undefined\n readonly showCloseButton: boolean\n readonly timeout: number\n}\n\ntype SnackbarAction = {\n /** What the action says. The page gives it a single word where it can. */\n readonly label: string\n /** Run when the action is pressed, just before the snackbar closes. */\n readonly onPress: () => void\n}\n\ntype SnackbarOptions = {\n /**\n * A single action shown after the message. Pressing it runs `onPress` and\n * closes the snackbar, which is what the page draws.\n */\n readonly action?: SnackbarAction\n /**\n * Run when the snackbar closes, however it was closed: by its timer, its\n * action or close button, `close`, `clear`, or a newer message added in its\n * place.\n */\n readonly onClose?: () => void\n /**\n * Whether a close button is shown at the end. The page draws one for a\n * message a reader may want out of the way before it expires.\n * @default false\n */\n readonly showCloseButton?: boolean\n /**\n * How long the snackbar is shown, in milliseconds. Defaults to five seconds\n * for a message alone, which Android lengthens from the reader's own\n * accessibility setting. A message with an action defaults to `0` and stays\n * until the action is taken or something closes it, as Material Design asks\n * of an actionable snackbar. The timer is paused while a pointer is over the\n * snackbar or one of its controls has focus, so a message never expires\n * while it is being read.\n *\n * `0` is the page's indefinite length: the snackbar stays until something\n * closes it. Give one of those an action or `showCloseButton`, or there is\n * no way to be rid of it.\n */\n readonly timeout?: number\n}\n\ntype SnackbarProps = Omit<ViewProps, 'children' | 'style'> & {\n /**\n * What a screen reader calls the button that dismisses a snackbar. It sits\n * here rather than on a message's own options because it does not vary by\n * message. Left out, it is the word React Aria gives web's in the\n * `I18nProvider`'s locale, or the device's outside one, \"Close\" in English.\n */\n readonly closeLabel?: string\n /** The queue the snackbar shows messages from. */\n readonly queue: SnackbarQueue\n readonly ref?: Ref<ComponentRef<typeof View>>\n readonly style?: StyleProp<ViewStyle>\n}\n\n/**\n * The queue a snackbar is shown from. Make one where the app can reach it, a\n * module of its own being the usual place, mount `Snackbar` once with it, and\n * call `add` from anywhere.\n *\n * ```ts\n * const messages = new Snackbar.Queue()\n * messages.add('Label', { action: { label: 'Undo', onPress: undo } })\n * ```\n */\nclass SnackbarQueue {\n #added = 0\n readonly #listeners = new Set<() => void>()\n #shown: null | ShownMessage = null\n\n /**\n * Shows a message in place of the one on screen, and returns the key that\n * closes it. The message it replaces is closed, so its `onClose` runs, and\n * it does not come back.\n */\n add(message: string, options: SnackbarOptions = {}): string {\n const { action, onClose, showCloseButton = false, timeout } = options\n\n this.clear()\n this.#added += 1\n\n const key = `snackbar-${String(this.#added)}`\n\n this.#shown = {\n action,\n key,\n message,\n onClose,\n showCloseButton,\n timeout: timeout ?? (action === undefined ? MESSAGE_MS : UNTIL_CLOSED),\n }\n this.#changed()\n\n return key\n }\n\n /** Closes the snackbar on screen, running its `onClose`. */\n clear() {\n if (this.#shown !== null) {\n this.close(this.#shown.key)\n }\n }\n\n /** Closes one snackbar, by the key `add` returned. */\n close(key: string) {\n const shown = this.#shown\n\n if (shown?.key !== key) {\n return\n }\n\n // Off the screen before `onClose` runs, so one that adds the next message\n // adds it to an empty queue.\n this.#shown = null\n this.#changed()\n shown.onClose?.()\n }\n\n /**\n * Calls `listener` whenever the message on screen changes, and returns what\n * stops it. `Snackbar` reads the queue through this and `visible`, and\n * neither is meant for a call site.\n */\n readonly subscribe = (listener: () => void) => {\n this.#listeners.add(listener)\n\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** The message on screen, or `null`. */\n readonly visible = () => this.#shown\n\n #changed() {\n for (const listener of this.#listeners) {\n listener()\n }\n }\n}\n\n// The handlers the strip hands its views, each built by a call rather than\n// declared in the component, which is what react-perf's\n// jsx-no-new-function-as-prop is after; the React Compiler memoises the result\n// on its inputs.\nfunction actionOf(action: SnackbarAction, close: () => void) {\n return () => {\n action.onPress()\n close()\n }\n}\n\nfunction closeOf(queue: SnackbarQueue, key: string) {\n return () => {\n queue.close(key)\n }\n}\n\nfunction flagOf(set: (value: boolean) => void, value: boolean) {\n return () => {\n set(value)\n }\n}\n\nfunction measureInto(height: SharedValue<number>) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n }\n}\n\n/**\n * A brief message at the bottom of the screen, with an optional action, with\n * web's props and values. Mount one of these with a `Snackbar.Queue`, and\n * call the queue's `add` to show a message from anywhere.\n *\n * ```tsx\n * const messages = new Snackbar.Queue()\n *\n * <Snackbar queue={messages} />\n * ```\n *\n * It draws in the overlay host of the app's outermost `ThemeScope`, above the\n * screen, and needs one around it. It is one region rather than one per\n * message, so mount it once, near the root, inside the `SafeAreaProvider`\n * whose insets it keeps clear of.\n *\n * `style`, `ref` and `testID` land on that region, a view along the bottom of\n * the screen that holds the message.\n */\nfunction Snackbar({\n closeLabel: ownCloseLabel,\n queue,\n style,\n ...props\n}: SnackbarProps) {\n const messages = useMessages()\n const closeLabel = ownCloseLabel ?? messages.close\n const theme = useTheme()\n const insets = use(SafeAreaInsetsContext) ?? NO_INSETS\n const wide = useWindowSizeClass() !== 'compact'\n const shown = useSyncExternalStore(queue.subscribe, queue.visible)\n // The message up when the snackbar mounted, which was on screen before it\n // rather than arriving, so it is put in place rather than risen to it: on\n // a screen that remounts the snackbar, and on a story seeded with one.\n const [standing] = useState(() => queue.visible()?.key)\n const { spacing } = theme\n\n return (\n <Overlay>\n <View\n aria-live={Platform.OS === 'web' ? 'polite' : undefined}\n {...props}\n style={composeStyles<ViewStyle>(\n stylesOf(theme).region,\n {\n paddingBottom: spacing.sm + insets.bottom,\n paddingLeft: spacing.sm + insets.left,\n paddingRight: spacing.sm + insets.right,\n },\n style,\n )}\n >\n {shown === null ? null : (\n <SnackbarStrip\n arrives={shown.key !== standing}\n closeLabel={closeLabel}\n key={shown.key}\n queue={queue}\n shown={shown}\n wide={wide}\n />\n )}\n </View>\n </Overlay>\n )\n}\n\n/**\n * One of the snackbar's two controls, a pill drawing the state layer, the\n * ripple and the ring in its own colour, over a target brought up to 48dp\n * from the `height` it is drawn at. It reports when it takes and loses focus,\n * which pauses the message's timer.\n */\nfunction SnackbarButton({\n 'aria-label': ariaLabel,\n children,\n color,\n height,\n onFocusChange,\n onPress,\n ring,\n style,\n}: {\n readonly 'aria-label'?: string\n readonly children: ReactNode\n readonly color: string\n readonly height: number\n readonly onFocusChange: (focused: boolean) => void\n readonly onPress: () => void\n readonly ring: ViewStyle\n readonly style: ViewStyle\n}) {\n const theme = useTheme()\n const corners = stylesOf(theme).control\n const feedback = usePressFeedback({\n color,\n corners,\n onBlur: () => {\n onFocusChange(false)\n },\n onFocus: () => {\n onFocusChange(true)\n },\n })\n\n return (\n <Pressable\n {...feedback.handlers}\n aria-label={ariaLabel}\n hitSlop={hitSlop(height)}\n onPress={onPress}\n role=\"button\"\n style={composeStyles<ViewStyle>(\n corners,\n style,\n feedback.ring === undefined ? undefined : ring,\n )}\n >\n {feedback.layer}\n {children}\n </Pressable>\n )\n}\n\n/** One message: the words, then whatever sits after them. */\nfunction SnackbarStrip({\n arrives,\n closeLabel,\n queue,\n shown,\n wide,\n}: {\n /** Whether the message rises into place, or is drawn there at once. */\n readonly arrives: boolean\n readonly closeLabel: string\n readonly queue: SnackbarQueue\n readonly shown: ShownMessage\n readonly wide: boolean\n}) {\n const theme = useTheme()\n const themed = stylesOf(theme)\n const body = useTypeRole('bodyMedium')\n const label = useTypeRole('labelLarge')\n const reduceMotion = useReducedMotion()\n const [focused, setFocused] = useState(false)\n const [hovered, setHovered] = useState(false)\n const { action, key, message, showCloseButton } = shown\n const { colors, motion, sizing } = theme\n\n // It rises its own height from the bottom edge as it fades in, which is\n // what the page draws and what says the message came from the edge of the\n // screen rather than appearing over it. Reduced motion puts it in its\n // settled place at once, as web's zeroed duration does.\n const rises = arrives && !reduceMotion\n const arrived = useSharedValue(rises ? 0 : 1)\n const height = useSharedValue(0)\n const rise = useAnimatedStyle(() => {\n 'worklet'\n const progress = arrived.get()\n\n return {\n opacity: progress,\n transform: [{ translateY: (1 - progress) * height.get() }],\n }\n })\n\n useEffect(() => {\n if (rises) {\n arrived.set(\n withTiming(1, {\n duration: motion.durationMedium1,\n easing: Easing.bezier(...motion.easingEmphasizedDecelerate),\n }),\n )\n }\n }, [arrived, motion, rises])\n\n useEffect(() => {\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n AccessibilityInfo.announceForAccessibilityWithOptions(message, {\n queue: true,\n })\n }\n }, [message])\n\n const close = closeOf(queue, key)\n\n useExpiry(useShownFor(shown.timeout), focused || hovered, close)\n\n return (\n <Animated.View\n onLayout={measureInto(height)}\n onPointerEnter={flagOf(setHovered, true)}\n onPointerLeave={flagOf(setHovered, false)}\n style={composeStyles<AnimatedStyle<ViewStyle>>(\n themed.strip,\n wide ? styles.wide : undefined,\n rise,\n )}\n >\n <View style={themed.message}>\n <NativeText style={composeStyles<TextStyle>(body, themed.messageText)}>\n {message}\n </NativeText>\n </View>\n {action === undefined && !showCloseButton ? null : (\n <View style={styles.controls}>\n {action === undefined ? null : (\n <SnackbarButton\n color={colors.inversePrimary}\n height={sizing.controlSm}\n onFocusChange={setFocused}\n onPress={actionOf(action, close)}\n ring={themed.actionRing}\n style={themed.action}\n >\n <NativeText\n style={composeStyles<TextStyle>(label, themed.actionLabel)}\n >\n {action.label}\n </NativeText>\n </SnackbarButton>\n )}\n {showCloseButton ? (\n <SnackbarButton\n aria-label={closeLabel}\n color={colors.inverseOnSurface}\n height={sizing.controlMd}\n onFocusChange={setFocused}\n onPress={close}\n ring={themed.closeRing}\n style={themed.close}\n >\n <CloseGlyph color={colors.inverseOnSurface} size={24} />\n </SnackbarButton>\n ) : null}\n </View>\n )}\n </Animated.View>\n )\n}\n\n// Closes a message once it has been shown for `duration` milliseconds,\n// counting only the time it was not paused. Nothing for a `duration` of 0,\n// which is until closed, or of `null`, which is not yet known.\nfunction useExpiry(\n duration: null | number,\n paused: boolean,\n expire: () => void,\n) {\n const left = useRef<null | number>(null)\n\n useEffect(() => {\n if (duration === null || duration === 0 || paused) {\n return undefined\n }\n\n const remaining = left.current ?? duration\n const started = Date.now()\n const timer = setTimeout(expire, remaining)\n\n return () => {\n clearTimeout(timer)\n left.current = remaining - (Date.now() - started)\n }\n }, [duration, expire, paused])\n}\n\n// How long a message is shown: its `timeout`, which Android lengthens from\n// the reader's \"Time to take action\" setting, as its own snackbar does, and\n// is `null` until Android has answered. Nothing is asked for a message shown\n// until closed.\nfunction useShownFor(timeout: number): null | number {\n const [recommended, setRecommended] = useState<null | number>(null)\n const asks = Platform.OS === 'android' && timeout > 0\n\n useEffect(() => {\n if (!asks) {\n return undefined\n }\n\n let current = true\n\n async function ask() {\n const milliseconds =\n await AccessibilityInfo.getRecommendedTimeoutMillis(timeout)\n\n if (current) {\n setRecommended(milliseconds)\n }\n }\n\n void ask()\n\n return () => {\n current = false\n }\n }, [asks, timeout])\n\n return asks ? recommended : timeout\n}\n\nSnackbar.Queue = SnackbarQueue\n\n// The queue is reached as `Snackbar.Queue` rather than exported beside the\n// component, as on web: a module exporting something that is not a component\n// is one fast refresh reloads whole.\nexport { Snackbar }\nexport type { SnackbarAction, SnackbarOptions, SnackbarProps, SnackbarQueue }\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { View, ViewProps } from "react-native";
|
|
2
|
+
import { ComponentRef, ReactNode, Ref } from "react";
|
|
3
|
+
//#region src/components/supporting-pane/index.d.ts
|
|
4
|
+
type SupportingPaneProps = Omit<ViewProps, 'children'> & {
|
|
5
|
+
/**
|
|
6
|
+
* The primary content. Takes whatever width the supporting pane leaves from
|
|
7
|
+
* the expanded window size up.
|
|
8
|
+
*/
|
|
9
|
+
readonly main?: ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* Which side of the main pane the supporting one takes from the expanded
|
|
12
|
+
* window size up. Below it the layout stacks either way, with the
|
|
13
|
+
* supporting pane under the main one.
|
|
14
|
+
* @default 'trailing'
|
|
15
|
+
*/
|
|
16
|
+
readonly placement?: 'leading' | 'trailing';
|
|
17
|
+
readonly ref?: Ref<ComponentRef<typeof View>>;
|
|
18
|
+
/**
|
|
19
|
+
* Content that only means something beside `main`. Sits below it in a
|
|
20
|
+
* compact or medium window, and beside it at a fixed 360dp from the
|
|
21
|
+
* expanded window size up.
|
|
22
|
+
*/
|
|
23
|
+
readonly supporting?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Material Design's supporting pane layout: a main pane and a companion that
|
|
27
|
+
* reflows underneath it when there is no room beside it.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <SupportingPane main={<Article />} supporting={<Related />} />
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Layout only: neither pane paints a surface or a border of its own, so what
|
|
34
|
+
* goes in them is composed at the call site out of Cards, lists or plain
|
|
35
|
+
* content. The main pane comes first in the tree in both shapes, so a screen
|
|
36
|
+
* reader reaches it before the content that supports it.
|
|
37
|
+
*
|
|
38
|
+
* `style`, `ref` and `testID` land on the outer `View`, and a `style` there
|
|
39
|
+
* applies over the layout's own.
|
|
40
|
+
*/
|
|
41
|
+
declare function SupportingPane({ main, placement, style, supporting, ...props }: SupportingPaneProps): import("react").JSX.Element;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { SupportingPane, type SupportingPaneProps };
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useTheme } from "../../theme/scope.js";
|
|
2
|
+
import { composeStyles, themedStyles } from "../../theme/styles.js";
|
|
3
|
+
import { useWindowSizeClass } from "../../hooks/useWindowSizeClass.js";
|
|
4
|
+
import { c } from "react/compiler-runtime";
|
|
5
|
+
import { StyleSheet, View } from "react-native";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/supporting-pane/index.tsx
|
|
8
|
+
const SUPPORTING_PANE = 360;
|
|
9
|
+
const BESIDE = /* @__PURE__ */ new Set([
|
|
10
|
+
"expanded",
|
|
11
|
+
"extraLarge",
|
|
12
|
+
"large"
|
|
13
|
+
]);
|
|
14
|
+
const styles = StyleSheet.create({
|
|
15
|
+
leading: { flexDirection: "row-reverse" },
|
|
16
|
+
main: {
|
|
17
|
+
flex: 1,
|
|
18
|
+
minWidth: 0
|
|
19
|
+
},
|
|
20
|
+
supporting: { width: SUPPORTING_PANE },
|
|
21
|
+
trailing: { flexDirection: "row" }
|
|
22
|
+
});
|
|
23
|
+
const stylesOf = themedStyles(({ spacing }) => StyleSheet.create({ root: { gap: spacing.xl } }));
|
|
24
|
+
/**
|
|
25
|
+
* Material Design's supporting pane layout: a main pane and a companion that
|
|
26
|
+
* reflows underneath it when there is no room beside it.
|
|
27
|
+
*
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <SupportingPane main={<Article />} supporting={<Related />} />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Layout only: neither pane paints a surface or a border of its own, so what
|
|
33
|
+
* goes in them is composed at the call site out of Cards, lists or plain
|
|
34
|
+
* content. The main pane comes first in the tree in both shapes, so a screen
|
|
35
|
+
* reader reaches it before the content that supports it.
|
|
36
|
+
*
|
|
37
|
+
* `style`, `ref` and `testID` land on the outer `View`, and a `style` there
|
|
38
|
+
* applies over the layout's own.
|
|
39
|
+
*/
|
|
40
|
+
function SupportingPane(t0) {
|
|
41
|
+
const $ = c(18);
|
|
42
|
+
let main;
|
|
43
|
+
let props;
|
|
44
|
+
let style;
|
|
45
|
+
let supporting;
|
|
46
|
+
let t1;
|
|
47
|
+
if ($[0] !== t0) {
|
|
48
|
+
({main, placement: t1, style, supporting, ...props} = t0);
|
|
49
|
+
$[0] = t0;
|
|
50
|
+
$[1] = main;
|
|
51
|
+
$[2] = props;
|
|
52
|
+
$[3] = style;
|
|
53
|
+
$[4] = supporting;
|
|
54
|
+
$[5] = t1;
|
|
55
|
+
} else {
|
|
56
|
+
main = $[1];
|
|
57
|
+
props = $[2];
|
|
58
|
+
style = $[3];
|
|
59
|
+
supporting = $[4];
|
|
60
|
+
t1 = $[5];
|
|
61
|
+
}
|
|
62
|
+
const placement = t1 === void 0 ? "trailing" : t1;
|
|
63
|
+
const own = stylesOf(useTheme());
|
|
64
|
+
const beside = BESIDE.has(useWindowSizeClass());
|
|
65
|
+
const row = placement === "leading" ? styles.leading : styles.trailing;
|
|
66
|
+
const T0 = View;
|
|
67
|
+
const t2 = composeStyles(own.root, beside ? row : null, style);
|
|
68
|
+
const t3 = beside ? styles.main : null;
|
|
69
|
+
let t4;
|
|
70
|
+
if ($[6] !== main || $[7] !== t3) {
|
|
71
|
+
t4 = /* @__PURE__ */ jsx(View, {
|
|
72
|
+
style: t3,
|
|
73
|
+
children: main
|
|
74
|
+
});
|
|
75
|
+
$[6] = main;
|
|
76
|
+
$[7] = t3;
|
|
77
|
+
$[8] = t4;
|
|
78
|
+
} else t4 = $[8];
|
|
79
|
+
const t5 = beside ? styles.supporting : null;
|
|
80
|
+
let t6;
|
|
81
|
+
if ($[9] !== supporting || $[10] !== t5) {
|
|
82
|
+
t6 = /* @__PURE__ */ jsx(View, {
|
|
83
|
+
style: t5,
|
|
84
|
+
children: supporting
|
|
85
|
+
});
|
|
86
|
+
$[9] = supporting;
|
|
87
|
+
$[10] = t5;
|
|
88
|
+
$[11] = t6;
|
|
89
|
+
} else t6 = $[11];
|
|
90
|
+
let t7;
|
|
91
|
+
if ($[12] !== T0 || $[13] !== props || $[14] !== t2 || $[15] !== t4 || $[16] !== t6) {
|
|
92
|
+
t7 = /* @__PURE__ */ jsxs(T0, {
|
|
93
|
+
...props,
|
|
94
|
+
style: t2,
|
|
95
|
+
children: [t4, t6]
|
|
96
|
+
});
|
|
97
|
+
$[12] = T0;
|
|
98
|
+
$[13] = props;
|
|
99
|
+
$[14] = t2;
|
|
100
|
+
$[15] = t4;
|
|
101
|
+
$[16] = t6;
|
|
102
|
+
$[17] = t7;
|
|
103
|
+
} else t7 = $[17];
|
|
104
|
+
return t7;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { SupportingPane };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["ComponentRef","ReactNode","Ref","ViewProps","ViewStyle","StyleSheet","View","WindowSizeClass","useWindowSizeClass","useTheme","composeStyles","themedStyles","SUPPORTING_PANE","BESIDE","ReadonlySet","Set","styles","create","leading","flexDirection","main","flex","minWidth","supporting","width","trailing","stylesOf","spacing","root","gap","xl","SupportingPaneProps","Omit","placement","ref","SupportingPane","t0","$","_c","props","style","t1","undefined","own","beside","has","row","T0","t2","t3","t4","t5","t6","t7","_c"],"sources":["../../../src/components/supporting-pane/index.tsx"],"sourcesContent":["import type { ComponentRef, ReactNode, Ref } from 'react'\nimport type { ViewProps, ViewStyle } from 'react-native'\n\nimport { StyleSheet, View } from 'react-native'\n\nimport type { WindowSizeClass } from '../../hooks/useWindowSizeClass'\n\nimport { useWindowSizeClass } from '../../hooks/useWindowSizeClass'\nimport { useTheme } from '../../theme/scope'\nimport { composeStyles, themedStyles } from '../../theme/styles'\n\n// kanso-ui's SupportingPane, Material Design's supporting pane canonical\n// layout. The supporting pane holds content that only means anything next to\n// the main pane, such as comments, related items or a tool panel, which is\n// what separates it from list-detail, where the second pane stands on its own.\n//\n// The page gives the layout two shapes. Below the expanded window size the\n// supporting pane is stacked under the main one, which is Material Design's\n// reflow strategy; the page's other compact option, a bottom sheet, is\n// Sheet's job at the call site. From expanded up, 840dp, the supporting pane\n// stands beside the main one at a fixed 360dp and the main pane takes the\n// rest. Native has no media queries, so the shape follows\n// `useWindowSizeClass()`, as ListDetail's does.\n//\n// **Which side the supporting pane takes is a choice of the side-by-side\n// shape alone.** The page puts it below the focus pane in medium windows and\n// under, and \"on the leading or trailing side of the focus pane\" from\n// expanded up, so `placement` moves the pane beside the main one and leaves\n// the stacked shape alone.\n//\n// **The main pane is first in the tree in both shapes, and a leading pane is\n// drawn before it by reversing the row.** That is web's departure too, for\n// web's reason: the page asks that the reading order match the arrangement\n// on screen, and no one order of the panes satisfies both of its rules. A\n// supporting pane written first would read before the main one when the\n// layout stacks, which is the one arrangement the page rules out, so the\n// break lands side by side instead, where a screen reader reaches the main\n// pane before the leading one drawn at its start.\n//\n// Both panes stay mounted in the same places in the tree in both shapes, so\n// a rotation or an unfolding keeps what either holds. A row runs from the\n// start edge, so under a right-to-left layout leading is the right-hand side,\n// with nothing here turning it over.\n\n// Material Design's width for the supporting pane, beside the main one.\nconst SUPPORTING_PANE = 360\n\n// The window sizes with room for both panes side by side.\nconst BESIDE: ReadonlySet<WindowSizeClass> = new Set([\n 'expanded',\n 'extraLarge',\n 'large',\n])\n\nconst styles = StyleSheet.create({\n leading: {\n flexDirection: 'row-reverse',\n },\n // The main pane beside the supporting one: whatever the fixed pane leaves,\n // which a long unbroken line inside cannot widen past its share.\n main: {\n flex: 1,\n minWidth: 0,\n },\n supporting: {\n width: SUPPORTING_PANE,\n },\n trailing: {\n flexDirection: 'row',\n },\n})\n\n// The 24dp between the panes, web's `spacing.xl`, in either shape.\nconst stylesOf = themedStyles(({ spacing }) =>\n StyleSheet.create({\n root: {\n gap: spacing.xl,\n },\n }),\n)\n\ntype SupportingPaneProps = Omit<ViewProps, 'children'> & {\n /**\n * The primary content. Takes whatever width the supporting pane leaves from\n * the expanded window size up.\n */\n readonly main?: ReactNode\n /**\n * Which side of the main pane the supporting one takes from the expanded\n * window size up. Below it the layout stacks either way, with the\n * supporting pane under the main one.\n * @default 'trailing'\n */\n readonly placement?: 'leading' | 'trailing'\n readonly ref?: Ref<ComponentRef<typeof View>>\n /**\n * Content that only means something beside `main`. Sits below it in a\n * compact or medium window, and beside it at a fixed 360dp from the\n * expanded window size up.\n */\n readonly supporting?: ReactNode\n}\n\n/**\n * Material Design's supporting pane layout: a main pane and a companion that\n * reflows underneath it when there is no room beside it.\n *\n * ```tsx\n * <SupportingPane main={<Article />} supporting={<Related />} />\n * ```\n *\n * Layout only: neither pane paints a surface or a border of its own, so what\n * goes in them is composed at the call site out of Cards, lists or plain\n * content. The main pane comes first in the tree in both shapes, so a screen\n * reader reaches it before the content that supports it.\n *\n * `style`, `ref` and `testID` land on the outer `View`, and a `style` there\n * applies over the layout's own.\n */\nfunction SupportingPane({\n main,\n placement = 'trailing',\n style,\n supporting,\n ...props\n}: SupportingPaneProps) {\n const own = stylesOf(useTheme())\n const beside = BESIDE.has(useWindowSizeClass())\n const row = placement === 'leading' ? styles.leading : styles.trailing\n\n return (\n <View\n {...props}\n style={composeStyles<ViewStyle>(own.root, beside ? row : null, style)}\n >\n <View style={beside ? styles.main : null}>{main}</View>\n <View style={beside ? styles.supporting : null}>{supporting}</View>\n </View>\n )\n}\n\nexport type { SupportingPaneProps }\n\nexport { SupportingPane }\n"],"mappings":";;;;;;;AA6CA,MAAMY,kBAAkB;AAGxB,MAAMC,yBAAuC,IAAIE,IAAI;CACnD;CACA;CACA;AAAO,CACR;AAED,MAAMC,SAASX,WAAWY,OAAO;CAC/BC,SAAS,EACPC,eAAe,cACjB;CAGAC,MAAM;EACJC,MAAM;EACNC,UAAU;CACZ;CACAC,YAAY,EACVC,OAAOZ,gBACT;CACAa,UAAU,EACRN,eAAe,MACjB;AACF,CAAC;AAGD,MAAMO,WAAWf,cAAc,EAAEgB,cAC/BtB,WAAWY,OAAO,EAChBW,MAAM,EACJC,KAAKF,QAAQG,GACf,EACF,CAAC,CACH;;;;;;;;;;;;;;;;;AAwCA,SAAAK,eAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAA,IAAAlB;CAAA,IAAAmB;CAAA,IAAAC;CAAA,IAAAjB;CAAA,IAAAkB;CAAA,IAAAJ,EAAA,OAAAD,IAAA;EAAwB,CAAA,CAAAhB,MAAAa,WAAAQ,IAAAD,OAAAjB,YAAA,GAAAgB,SAAAH;EAMFC,EAAA,KAAAD;EAAAC,EAAA,KAAAjB;EAAAiB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAd;EAAAc,EAAA,KAAAI;CAAA,OAAA;EAAArB,OAAAiB,EAAA;EAAAE,QAAAF,EAAA;EAAAG,QAAAH,EAAA;EAAAd,aAAAc,EAAA;EAAAI,KAAAJ,EAAA;CAAA;CAJpB,MAAAJ,YAAAQ,OAAAC,KAAAA,IAAA,aAAAD;CAKA,MAAAE,MAAYjB,SAASjB,SAAS,CAAC;CAC/B,MAAAmC,SAAe/B,OAAMgC,IAAKrC,mBAAmB,CAAC;CAC9C,MAAAsC,MAAYb,cAAc,YAAYjB,OAAME,UAAWF,OAAMS;CAG1D,MAAAsB,KAAAzC;CAEQ,MAAA0C,KAAAtC,cAAyBiC,IAAGf,MAAOgB,SAAAE,MAAA,MAAqBN,KAAK;CAEvD,MAAAS,KAAAL,SAAS5B,OAAMI,OAAf;CAA2B,IAAA8B;CAAA,IAAAb,EAAA,OAAAjB,QAAAiB,EAAA,OAAAY,IAAA;EAAxCC,KAAA,oBAAC,MAAD;GAAa,OAAAD;GAA8B7B,UAAAA;EAAtC,CAAA;EAAkDiB,EAAA,KAAAjB;EAAAiB,EAAA,KAAAY;EAAAZ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAC1C,MAAAc,KAAAP,SAAS5B,OAAMO,aAAf;CAAiC,IAAA6B;CAAA,IAAAf,EAAA,OAAAd,cAAAc,EAAA,QAAAc,IAAA;EAA9CC,KAAA,oBAAC,MAAD;GAAa,OAAAD;GAAoC5B,UAAAA;EAA5C,CAAA;EAA8Dc,EAAA,KAAAd;EAAAc,EAAA,MAAAc;EAAAd,EAAA,MAAAe;CAAA,OAAAA,KAAAf,EAAA;CAAA,IAAAgB;CAAA,IAAAhB,EAAA,QAAAU,MAAAV,EAAA,QAAAE,SAAAF,EAAA,QAAAW,MAAAX,EAAA,QAAAa,MAAAb,EAAA,QAAAe,IAAA;EALrEC,KAAA,qBAAC,IAAD;GAAK,GACCd;GACG,OAAAS;GAFT,UAAA,CAIEE,IACAE,EALG;;EAMEf,EAAA,MAAAU;EAAAV,EAAA,MAAAE;EAAAF,EAAA,MAAAW;EAAAX,EAAA,MAAAa;EAAAb,EAAA,MAAAe;EAAAf,EAAA,MAAAgB;CAAA,OAAAA,KAAAhB,EAAA;CAAA,OANPgB;AAMO"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { View, ViewProps } from "react-native";
|
|
2
|
+
import { ComponentRef, ReactNode, Ref } from "react";
|
|
3
|
+
//#region src/components/tag/index.d.ts
|
|
4
|
+
type TagProps = ViewProps & {
|
|
5
|
+
/**
|
|
6
|
+
* What the tag says. A string or a number is drawn as its label, and runs
|
|
7
|
+
* of them as one label; anything else, such as an icon, is drawn as given,
|
|
8
|
+
* handed the tag's colour and size through `useIconStyle()`.
|
|
9
|
+
*/
|
|
10
|
+
readonly children?: ReactNode;
|
|
11
|
+
readonly ref?: Ref<ComponentRef<typeof View>>;
|
|
12
|
+
/**
|
|
13
|
+
* Which colour role the tag carries. `neutral` states without ranking; the
|
|
14
|
+
* other three read as good, bad, or worth noticing.
|
|
15
|
+
*
|
|
16
|
+
* Each is a container under its own on-colour, legible on the surface
|
|
17
|
+
* family, which is what the library's own tokens and every demo scheme are
|
|
18
|
+
* held to. Over a container of its own the pair is one colour family on top
|
|
19
|
+
* of another and is not guaranteed, so a custom theme placing a tag there
|
|
20
|
+
* has to check that pair itself.
|
|
21
|
+
* @default 'neutral'
|
|
22
|
+
*/
|
|
23
|
+
readonly tone?: TagTone;
|
|
24
|
+
/**
|
|
25
|
+
* How much weight the tag pulls. `filled` sits on a container of its own;
|
|
26
|
+
* `outlined` is a rule on the surface, for a tag that should not compete
|
|
27
|
+
* with the thing it labels.
|
|
28
|
+
* @default 'filled'
|
|
29
|
+
*/
|
|
30
|
+
readonly variant?: TagVariant;
|
|
31
|
+
};
|
|
32
|
+
type TagTone = 'negative' | 'neutral' | 'positive' | 'primary';
|
|
33
|
+
type TagVariant = 'filled' | 'outlined';
|
|
34
|
+
/**
|
|
35
|
+
* A short, read-only label attached to something else, such as a count or a
|
|
36
|
+
* status beside a row's headline. It takes no press: for a label that can be
|
|
37
|
+
* selected, reach for Chip.
|
|
38
|
+
*
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <ListItem trailing={<Tag tone="positive">Label</Tag>}>Headline</ListItem>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* The label is set in label small with tabular figures, on one line. An icon
|
|
44
|
+
* given beside it is drawn at the label's line height in the label's colour,
|
|
45
|
+
* so a tag with an icon is the same height as one without. A screen reader
|
|
46
|
+
* reads the label where the tag sits; the tag adds no role of its own.
|
|
47
|
+
*
|
|
48
|
+
* Like any view, a tag stretches across a column it sits in on its own, so a
|
|
49
|
+
* call site placing one there sets `alignSelf`. A row slot, such as
|
|
50
|
+
* ListItem's trailing one, already holds it at its own width.
|
|
51
|
+
*
|
|
52
|
+
* `style`, `ref` and `testID` land on the `View`, and a `style` there applies
|
|
53
|
+
* over the tag's own.
|
|
54
|
+
*/
|
|
55
|
+
declare function Tag({ children, style, tone, variant, ...props }: TagProps): import("react").JSX.Element;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { Tag, type TagProps, type TagTone, type TagVariant };
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { IconStyleContext } from "../../glyphs/icon-style.js";
|
|
2
|
+
import { useTheme } from "../../theme/scope.js";
|
|
3
|
+
import { composeStyles, themedStyles } from "../../theme/styles.js";
|
|
4
|
+
import { useTypeRole } from "../../typography/roles.js";
|
|
5
|
+
import { c } from "react/compiler-runtime";
|
|
6
|
+
import { StyleSheet, Text, View } from "react-native";
|
|
7
|
+
import { Children } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/tag/index.tsx
|
|
10
|
+
const styles = StyleSheet.create({ label: {
|
|
11
|
+
flexShrink: 0,
|
|
12
|
+
fontVariant: ["tabular-nums"]
|
|
13
|
+
} });
|
|
14
|
+
const stylesOf = themedStyles(({ radii, spacing }) => StyleSheet.create({ base: {
|
|
15
|
+
alignItems: "center",
|
|
16
|
+
borderRadius: radii.sm,
|
|
17
|
+
borderWidth: 1,
|
|
18
|
+
flexDirection: "row",
|
|
19
|
+
flexShrink: 0,
|
|
20
|
+
gap: spacing.xs,
|
|
21
|
+
paddingHorizontal: spacing.sm,
|
|
22
|
+
paddingVertical: spacing.xxs
|
|
23
|
+
} }));
|
|
24
|
+
const tonesOf = themedStyles(({ colors }) => ({
|
|
25
|
+
filled: {
|
|
26
|
+
negative: toneOf(colors.negativeContainer, "transparent", colors.onNegativeContainer),
|
|
27
|
+
neutral: toneOf(colors.surfaceContainerHighest, "transparent", colors.onSurfaceVariant),
|
|
28
|
+
positive: toneOf(colors.positiveContainer, "transparent", colors.onPositiveContainer),
|
|
29
|
+
primary: toneOf(colors.primaryContainer, "transparent", colors.onPrimaryContainer)
|
|
30
|
+
},
|
|
31
|
+
outlined: {
|
|
32
|
+
negative: toneOf("transparent", colors.negative, colors.negative),
|
|
33
|
+
neutral: toneOf("transparent", colors.outlineVariant, colors.onSurfaceVariant),
|
|
34
|
+
positive: toneOf("transparent", colors.positive, colors.positive),
|
|
35
|
+
primary: toneOf("transparent", colors.primary, colors.primary)
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
/**
|
|
39
|
+
* The children in order, with each run of strings and numbers drawn as one
|
|
40
|
+
* label, so `{count} items` is one Text rather than three. A React Native
|
|
41
|
+
* string has to sit inside a Text, and anything else is drawn as given. A
|
|
42
|
+
* label is keyed by where it falls among what is drawn, which a render of the
|
|
43
|
+
* same children always repeats.
|
|
44
|
+
*/
|
|
45
|
+
function contentOf(children, label) {
|
|
46
|
+
const content = [];
|
|
47
|
+
let text;
|
|
48
|
+
const flush = () => {
|
|
49
|
+
if (text !== void 0) {
|
|
50
|
+
content.push(/* @__PURE__ */ jsx(Text, {
|
|
51
|
+
numberOfLines: 1,
|
|
52
|
+
style: label,
|
|
53
|
+
children: text
|
|
54
|
+
}, `label-${String(content.length)}`));
|
|
55
|
+
text = void 0;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
for (const child of Children.toArray(children)) if (typeof child === "string" || typeof child === "number" || typeof child === "bigint") text = (text ?? "") + String(child);
|
|
59
|
+
else {
|
|
60
|
+
flush();
|
|
61
|
+
content.push(child);
|
|
62
|
+
}
|
|
63
|
+
flush();
|
|
64
|
+
return content;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The colour and size the tag hands an icon beside its label. Built by a call
|
|
68
|
+
* rather than written inline, which is what react-perf's
|
|
69
|
+
* jsx-no-new-object-as-prop is after; the React Compiler memoises the result
|
|
70
|
+
* on its inputs.
|
|
71
|
+
*/
|
|
72
|
+
function iconStyleOf(color, size) {
|
|
73
|
+
return {
|
|
74
|
+
color,
|
|
75
|
+
size
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A short, read-only label attached to something else, such as a count or a
|
|
80
|
+
* status beside a row's headline. It takes no press: for a label that can be
|
|
81
|
+
* selected, reach for Chip.
|
|
82
|
+
*
|
|
83
|
+
* ```tsx
|
|
84
|
+
* <ListItem trailing={<Tag tone="positive">Label</Tag>}>Headline</ListItem>
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* The label is set in label small with tabular figures, on one line. An icon
|
|
88
|
+
* given beside it is drawn at the label's line height in the label's colour,
|
|
89
|
+
* so a tag with an icon is the same height as one without. A screen reader
|
|
90
|
+
* reads the label where the tag sits; the tag adds no role of its own.
|
|
91
|
+
*
|
|
92
|
+
* Like any view, a tag stretches across a column it sits in on its own, so a
|
|
93
|
+
* call site placing one there sets `alignSelf`. A row slot, such as
|
|
94
|
+
* ListItem's trailing one, already holds it at its own width.
|
|
95
|
+
*
|
|
96
|
+
* `style`, `ref` and `testID` land on the `View`, and a `style` there applies
|
|
97
|
+
* over the tag's own.
|
|
98
|
+
*/
|
|
99
|
+
function Tag(t0) {
|
|
100
|
+
const $ = c(28);
|
|
101
|
+
let children;
|
|
102
|
+
let props;
|
|
103
|
+
let style;
|
|
104
|
+
let t1;
|
|
105
|
+
let t2;
|
|
106
|
+
if ($[0] !== t0) {
|
|
107
|
+
({children, style, tone: t1, variant: t2, ...props} = t0);
|
|
108
|
+
$[0] = t0;
|
|
109
|
+
$[1] = children;
|
|
110
|
+
$[2] = props;
|
|
111
|
+
$[3] = style;
|
|
112
|
+
$[4] = t1;
|
|
113
|
+
$[5] = t2;
|
|
114
|
+
} else {
|
|
115
|
+
children = $[1];
|
|
116
|
+
props = $[2];
|
|
117
|
+
style = $[3];
|
|
118
|
+
t1 = $[4];
|
|
119
|
+
t2 = $[5];
|
|
120
|
+
}
|
|
121
|
+
const tone = t1 === void 0 ? "neutral" : t1;
|
|
122
|
+
const variant = t2 === void 0 ? "filled" : t2;
|
|
123
|
+
const theme = useTheme();
|
|
124
|
+
const role = useTypeRole("labelSmall");
|
|
125
|
+
let T0;
|
|
126
|
+
let T1;
|
|
127
|
+
let t3;
|
|
128
|
+
let t4;
|
|
129
|
+
let t5;
|
|
130
|
+
let t6;
|
|
131
|
+
if ($[6] !== children || $[7] !== props || $[8] !== role || $[9] !== style || $[10] !== theme || $[11] !== tone || $[12] !== variant) {
|
|
132
|
+
const toned = tonesOf(theme)[variant][tone];
|
|
133
|
+
T1 = View;
|
|
134
|
+
t5 = props;
|
|
135
|
+
t6 = composeStyles(stylesOf(theme).base, toned.container, style);
|
|
136
|
+
T0 = IconStyleContext;
|
|
137
|
+
t3 = iconStyleOf(toned.content, theme.typography.labelSmallLineHeight);
|
|
138
|
+
t4 = contentOf(children, composeStyles(role, styles.label, toned.label));
|
|
139
|
+
$[6] = children;
|
|
140
|
+
$[7] = props;
|
|
141
|
+
$[8] = role;
|
|
142
|
+
$[9] = style;
|
|
143
|
+
$[10] = theme;
|
|
144
|
+
$[11] = tone;
|
|
145
|
+
$[12] = variant;
|
|
146
|
+
$[13] = T0;
|
|
147
|
+
$[14] = T1;
|
|
148
|
+
$[15] = t3;
|
|
149
|
+
$[16] = t4;
|
|
150
|
+
$[17] = t5;
|
|
151
|
+
$[18] = t6;
|
|
152
|
+
} else {
|
|
153
|
+
T0 = $[13];
|
|
154
|
+
T1 = $[14];
|
|
155
|
+
t3 = $[15];
|
|
156
|
+
t4 = $[16];
|
|
157
|
+
t5 = $[17];
|
|
158
|
+
t6 = $[18];
|
|
159
|
+
}
|
|
160
|
+
let t7;
|
|
161
|
+
if ($[19] !== T0 || $[20] !== t3 || $[21] !== t4) {
|
|
162
|
+
t7 = /* @__PURE__ */ jsx(T0, {
|
|
163
|
+
value: t3,
|
|
164
|
+
children: t4
|
|
165
|
+
});
|
|
166
|
+
$[19] = T0;
|
|
167
|
+
$[20] = t3;
|
|
168
|
+
$[21] = t4;
|
|
169
|
+
$[22] = t7;
|
|
170
|
+
} else t7 = $[22];
|
|
171
|
+
let t8;
|
|
172
|
+
if ($[23] !== T1 || $[24] !== t5 || $[25] !== t6 || $[26] !== t7) {
|
|
173
|
+
t8 = /* @__PURE__ */ jsx(T1, {
|
|
174
|
+
...t5,
|
|
175
|
+
style: t6,
|
|
176
|
+
children: t7
|
|
177
|
+
});
|
|
178
|
+
$[23] = T1;
|
|
179
|
+
$[24] = t5;
|
|
180
|
+
$[25] = t6;
|
|
181
|
+
$[26] = t7;
|
|
182
|
+
$[27] = t8;
|
|
183
|
+
} else t8 = $[27];
|
|
184
|
+
return t8;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* One tone's styles: the container's fill and rule, the label's colour, and
|
|
188
|
+
* that colour on its own for an icon beside the label.
|
|
189
|
+
*/
|
|
190
|
+
function toneOf(background, border, content) {
|
|
191
|
+
return {
|
|
192
|
+
content,
|
|
193
|
+
...StyleSheet.create({
|
|
194
|
+
container: {
|
|
195
|
+
backgroundColor: background,
|
|
196
|
+
borderColor: border
|
|
197
|
+
},
|
|
198
|
+
label: { color: content }
|
|
199
|
+
})
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { Tag };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=index.js.map
|