UNPKG

@react-spectrum/s2

Version:
1 lines 9.68 kB
{"mappings":"AC4BwB;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAqIA;;;;EAAA;;;;EAQA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAKG;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;EAAA;;;;;AAlJH;EAkJG;;;;EAAA;;;;;AAlJH;EAAA;IAAA;;;;;;AAkJG;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;;;;;AAAA;EAAA;IAAA","sources":["a1b352514784d267","packages/@react-spectrum/s2/src/ActionBar.tsx"],"sourcesContent":["@import \"ae871d7b3899fe46\";\n@import \"b12d7526a8920c7d\";\n@import \"ac539ec7d1ef9296\";\n@import \"e8e91cecf3846843\";\n","/*\n * Copyright 2024 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {ActionButtonGroup} from './ActionButtonGroup';\nimport {announce} from '@react-aria/live-announcer';\nimport {CloseButton} from './CloseButton';\nimport {ContextValue, SlotProps} from 'react-aria-components';\nimport {createContext, ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {DOMRef, DOMRefValue, Key} from '@react-types/shared';\nimport {FocusScope, useKeyboard} from 'react-aria';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {style} from '../style' with {type: 'macro'};\nimport {useControlledState} from '@react-stately/utils';\nimport {useDOMRef} from '@react-spectrum/utils';\nimport {useEnterAnimation, useExitAnimation, useObjectRef, useResizeObserver} from '@react-aria/utils';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\nimport {useSpectrumContextProps} from './useSpectrumContextProps';\n\nconst actionBarStyles = style({\n borderRadius: 'lg',\n '--s2-container-bg': {\n type: 'backgroundColor',\n value: {\n default: 'elevated',\n isEmphasized: 'neutral'\n }\n },\n backgroundColor: '--s2-container-bg',\n boxShadow: 'elevated',\n boxSizing: 'border-box',\n outlineStyle: 'solid',\n outlineWidth: 1,\n outlineOffset: -1,\n outlineColor: {\n default: 'gray-200',\n isEmphasized: 'transparent',\n forcedColors: 'ButtonBorder'\n },\n paddingX: 8,\n paddingY: 12,\n display: 'flex',\n gap: 16,\n alignItems: 'center',\n position: {\n isInContainer: 'absolute'\n },\n bottom: 0,\n insetStart: 8,\n '--insetEnd': {\n type: 'insetEnd',\n value: 8\n },\n width: {\n default: 'full',\n isInContainer: 'auto'\n },\n marginX: 'auto',\n maxWidth: 960,\n transition: 'transform',\n transitionDuration: 200,\n translateY: {\n default: -8,\n isEntering: 'full',\n isExiting: 'full'\n }\n});\n\nexport interface ActionBarProps extends SlotProps {\n /** A list of ActionButtons to display. */\n children: ReactNode,\n /** Whether the ActionBar should be displayed with a emphasized style. */\n isEmphasized?: boolean,\n /** The number of selected items that the ActionBar is currently linked to. If 0, the ActionBar is hidden. */\n selectedItemCount?: number | 'all',\n /** Handler that is called when the ActionBar clear button is pressed. */\n onClearSelection?: () => void,\n /** A ref to the scrollable element the ActionBar appears above. */\n scrollRef?: RefObject<HTMLElement | null>\n}\n\nexport const ActionBarContext = createContext<ContextValue<Partial<ActionBarProps>, DOMRefValue<HTMLDivElement>>>(null);\n\nexport const ActionBar = forwardRef(function ActionBar(props: ActionBarProps, ref: DOMRef<HTMLDivElement>) {\n [props, ref] = useSpectrumContextProps(props, ref, ActionBarContext);\n let domRef = useDOMRef(ref);\n\n let isOpen = props.selectedItemCount !== 0;\n let isExiting = useExitAnimation(domRef, isOpen && props.scrollRef != null);\n if (!isOpen && !isExiting) {\n return null;\n }\n\n return <ActionBarInner {...props} ref={domRef} isExiting={isExiting} />;\n});\n\nconst ActionBarInner = forwardRef(function ActionBarInner(props: ActionBarProps & {isExiting: boolean}, ref: ForwardedRef<HTMLDivElement | null>) {\n let {isEmphasized, selectedItemCount = 0, children, onClearSelection, isExiting} = props;\n let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/s2');\n\n // Store the last count greater than zero so that we can retain it while rendering the fade-out animation.\n let [lastCount, setLastCount] = useState(selectedItemCount);\n if ((selectedItemCount === 'all' || selectedItemCount > 0) && selectedItemCount !== lastCount) {\n setLastCount(selectedItemCount);\n }\n\n // Measure the width of the collection's scrollbar and offset the action bar by that amount.\n let scrollRef = props.scrollRef;\n let [scrollbarWidth, setScrollbarWidth] = useState(0);\n let updateScrollbarWidth = useCallback(() => {\n let el = scrollRef?.current;\n if (el) {\n let w = el.offsetWidth - el.clientWidth;\n setScrollbarWidth(w);\n }\n }, [scrollRef]);\n\n useResizeObserver({\n ref: scrollRef,\n onResize: updateScrollbarWidth\n });\n\n let {keyboardProps} = useKeyboard({\n onKeyDown(e) {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClearSelection?.();\n } else {\n e.continuePropagation();\n }\n }\n });\n\n // Announce \"actions available\" on mount.\n let isInitial = useRef(true);\n useEffect(() => {\n if (isInitial.current && scrollRef) {\n isInitial.current = false;\n announce(stringFormatter.format('actionbar.actionsAvailable'));\n }\n }, [stringFormatter, scrollRef]);\n\n let objectRef = useObjectRef(ref);\n let isEntering = useEnterAnimation(objectRef, !!scrollRef);\n\n return (\n <FocusScope restoreFocus>\n <div\n ref={objectRef}\n {...keyboardProps}\n className={actionBarStyles({isEmphasized, isInContainer: !!scrollRef, isEntering, isExiting})}\n style={{insetInlineEnd: `calc(var(--insetEnd) + ${scrollbarWidth}px)`}}>\n <div className={style({order: 1, marginStart: 'auto'})}>\n <ActionButtonGroup\n staticColor={isEmphasized ? 'auto' : undefined}\n isQuiet\n aria-label={stringFormatter.format('actionbar.actions')}>\n {children}\n </ActionButtonGroup>\n </div>\n <div className={style({order: 0, display: 'flex', alignItems: 'center', gap: 4})}>\n <CloseButton\n staticColor={isEmphasized ? 'auto' : undefined}\n aria-label={stringFormatter.format('actionbar.clearSelection')}\n onPress={() => onClearSelection?.()} />\n <span className={style({font: 'ui', color: {default: 'neutral', isEmphasized: 'gray-25'}})({isEmphasized})}>\n {lastCount === 'all'\n ? stringFormatter.format('actionbar.selectedAll')\n : stringFormatter.format('actionbar.selected', {count: lastCount})}\n </span>\n </div>\n </div>\n </FocusScope>\n );\n});\n\ninterface ActionBarContainerHookProps {\n selectedKeys?: 'all' | Iterable<Key>,\n defaultSelectedKeys?: 'all' | Iterable<Key>,\n onSelectionChange?: (keys: Set<Key>) => void,\n renderActionBar?: (selectedKeys: 'all' | Set<Key>) => ReactElement,\n scrollRef?: RefObject<HTMLElement | null>\n}\n\ninterface ActionBarContainerHookResult {\n selectedKeys: 'all' | Iterable<Key>,\n onSelectionChange: (keys: 'all' | Iterable<Key>) => void,\n actionBar: ReactElement,\n actionBarHeight: number\n}\n\nexport function useActionBarContainer(props: ActionBarContainerHookProps): ActionBarContainerHookResult {\n let {renderActionBar, scrollRef} = props;\n let [selectedKeys, setSelectedKeys] = useControlledState(props.selectedKeys, props.defaultSelectedKeys || new Set(), props.onSelectionChange);\n let selectedKeysSet = useMemo(() => selectedKeys === 'all' ? selectedKeys as 'all' : new Set(selectedKeys), [selectedKeys]);\n let actionBar = useMemo(() => renderActionBar?.(selectedKeysSet), [renderActionBar, selectedKeysSet]);\n let selectedItemCount = selectedKeysSet === 'all' ? 'all' as const : selectedKeysSet.size;\n let [actionBarHeight, setActionBarHeight] = useState(0);\n let actionBarRef = useCallback((ref: DOMRefValue | null) => {\n let actionBar = ref?.UNSAFE_getDOMNode();\n if (actionBar) {\n setActionBarHeight(actionBar.offsetHeight + 8);\n } else {\n setActionBarHeight(0);\n }\n }, []);\n\n let actionBarContext = useMemo(() => ({\n ref: actionBarRef,\n scrollRef,\n selectedItemCount,\n onClearSelection: () => setSelectedKeys(new Set())\n }), [scrollRef, actionBarRef, selectedItemCount, setSelectedKeys]);\n\n let wrappedActionBar = useMemo(() => (\n <ActionBarContext.Provider value={actionBarContext}>\n {actionBar}\n </ActionBarContext.Provider>\n ), [actionBarContext, actionBar]);\n\n return {\n selectedKeys,\n onSelectionChange: setSelectedKeys,\n actionBar: wrappedActionBar,\n actionBarHeight\n };\n}\n"],"names":[],"version":3,"file":"ActionBar.css.map"}